2009-03-22 16 views
8

Tôi gặp sự cố khi quản lý đường dẫn dài. Làm thế nào tôi có thể nhận được một cách nhanh chóng để những con đường nhưLàm thế nào để quản lý đường dẫn dài trong Bash?

/Users/User/.../.../.../.../.../Dev/C/card.c 

tôi đã cố gắng một bí danh

alias cd C='cd /Users/User/.../.../.../.../.../Dev/C' 

nhưng tôi không thể làm bí danh cho hai từ riêng biệt. Tôi có danh sách dài các bí danh và đường dẫn Bash trong CDPATH, vì vậy tôi đang do dự để làm cho chúng nhiều hơn. Làm cách nào để quản lý đường dẫn dài?

[Ý tưởng cho Trả lời]

trả lời Các litb người dùng đã tiết lộ một số vấn đề của tôi trong việc quản lý. Mọi thứ, chẳng hạn như "CTRL + R", "! -3: 1: 2: 4: x" và "tìm kiếm gia tăng", rất khó cho tôi. Họ có thể giúp đỡ trong việc điều hướng các thư mục dài và, theo nghĩa, quản lý.

Trả lời

24

Cân nhắc sử dụng các liên kết tượng trưng. Tôi có một thư mục ~/work/ nơi tôi đặt liên kết tượng trưng cho tất cả các dự án hiện tại của mình.

Bạn cũng có thể sử dụng các biến shell:

c='/Users/User/.../.../.../.../.../Dev/C' 

Sau đó:

cd "$c" 
+0

Lời cảm ơn tuyệt vời dành cho mẹo! –

+0

Tôi gặp phải sự cố khi tôi đổi thư mục. Tôi đã giải quyết nó với các biến shell và symlink: Dev = '/ Users/User/Dev /' site = '$ Dev /.../ C /.../ site' Chúng là sự kết hợp rất mạnh mẽ :) –

+0

Đừng quên rằng bạn nên "báo" mở rộng tham số của mình. cd "$ c" Nó cũng làm cho nó hơi cồng kềnh một lần nữa; nhưng bạn ** nên ** dính vào thực hành này. Vì vậy, yeah, symlink có lẽ là cách tốt nhất. – lhunath

6

Tạo liên kết tượng trưng trong thư mục chính của bạn (hoặc ở một nơi khác lựa chọn của bạn)

ln -slongDirectoryPath ~/MySymLinkName

Xem man ln để biết thêm chi tiết.

+0

Xin lỗi, tôi mất ba ngày để hiểu câu trả lời của bạn. Đối với người đọc trong tương lai, các dấu hiệu> và

+0

Xin lỗi về lỗi não đó. Tôi sẽ chỉnh sửa văn bản để sử dụng chữ nghiêng thay vì <...> –

+0

Tôi chỉ biết rằng các liên kết tượng trưng cũng được gọi là "softlinks" :-) –

1

Nhìn vào pushd, cho phép bạn duy trì một chồng thư mục mà bạn có thể đẩy lên, bật tắt của, hoặc sắp xếp lại.

+0

Mẹo hay, tôi thường xuyên sử dụng chúng. Tôi đã thêm một số lệnh Bash của tôi làm câu trả lời. Nó có vẻ tốt hơn trong cách. –

+0

Bạn được chào đón. Không có viên đạn bạc cho những gì bạn đang làm, nhưng nếu bạn đặt cùng một số gợi ý rất hay mà bạn thấy ở đây, bạn sẽ khá hiệu quả! – JasonSmith

5

lẽ là giải pháp đơn giản nhất là sử dụng:

alias cdc='cd /Users/User/.../.../.../.../.../Dev/C' 
alias cdbin='cd /Users/User/.../.../.../.../.../Dev/bin' 
alias cdtst='cd /Users/User/.../.../.../.../.../Dev/tst' 

nếu bạn chỉ thực sự làm việc trên một dự án cùng một lúc. Nếu bạn làm việc trên nhiều dự án, bạn có thể có một bí danh khác đã thay đổi các thư mục trong các bí danh trên.

Vì vậy, bạn muốn sử dụng cái gì đó như:

proj game17 
cdc 
make 
proj roman_numerals 
cdbin 
rm -f * 
proj game17 ; cdc 

Do đây là một điều hữu ích để có, tôi quyết định đặt cùng một loạt các kịch bản có thể được sử dụng. Tất cả chúng đều dựa trên một tệp cấu hình mà bạn đặt trong thư mục chính của bạn, cùng với các bí danh cho các tập lệnh nguồn. Các tập tin "~/.cdx_data" có dạng:

scrabble:top=~/dev/scrabble 
scrabble:src=~/dev/scrabble/src 
scrabble:bin=~/dev/scrabble/bin 

sudoku:top=~/dev/scrabble 
sudoku:src=~/dev/scrabble/src 
sudoku:bin=~/dev/scrabble/bin 
sudoku:data=~/dev/scrabble/data 

và liệt kê tất cả các dự án có liên quan (gải và sodoku trong trường hợp này) và thư mục của họ (có thể khác nhau đối với từng dự án, nhưng có đầu, bin, src và dữ liệu trong ví dụ này).

Hành động đầu tiên là khởi tạo thứ, vì vậy đặt:

. ~/.cdx_init 

ở phần cuối của bạn.bash_profile và tạo tệp "~/.cdx_init" dưới dạng:

alias cdxl='. ~/.cdx_list' 
alias projl='. ~/.cdx_projlist' 
alias cdx='. ~/.cdx_goto' 
alias proj='. ~/.cdx_proj' 

Điều này thiết lập bốn bí danh để nguồn các tệp mà tôi sẽ đưa vào bên dưới. Cách sử dụng là:

cdxl  - List all directories in current project. 

projl - List all projects. 

proj  - Show current project. 
proj <p> - Set current project to <p> (if allowed). 

cdx  - Show current project/directory and expected/actual real 
      directory, since they can get out of sync if you mix cd and cdx. 
cdx . - Set actual real directory to expected directory (in other words, 
      get them back into sync). 
cdx <d> - Set directory to <d> (if allowed). 

Tập lệnh thực tế theo sau. Đầu tiên, ".cdx_list" chỉ liệt kê các thư mục được phép trong dự án hiện tại (các đường ống được chia thành nhiều dòng để dễ đọc nhưng tất cả chúng đều nằm trên một dòng).

echo "Possible directories are:" 
cat ~/.cdx_data 
    | grep "^${CDX_PROJ}:" 
    | sed -e 's/^.*://' -e 's/=.*$//' 
    | sort -u 
    | sed 's/^/ /' 

Tương tự, ".cdx_projlist" thấy tất cả các dự án khả thi:

echo "Possible projects are:" 
cat ~/.cdx_data 
    | grep ':' 
    | sed 's/:.*$//' 
    | sort -u 
    | sed 's/^/ /' 

Trong kịch bản thịt, ".cdx_proj" bộ và/hoặc cho thấy dự án hiện tại:

if [[ "$1" != "" ]] ; then 
    grep "^$1:" ~/.cdx_data >/dev/null 2>&1 
    if [[ $? != 0 ]] ; then 
     echo "No project name '$1'." 
     projl 
    else 
     export CDX_PROJ="$1" 
    fi 
fi 
echo "Current project is: [${CDX_PROJ}]" 

".cdx_goto" là như nhau cho các thư mục trong dự án:

if [[ "$1" == "." ]] ; then 
    CDX_TMP="${CDX_DIR}" 
else 
    CDX_TMP="$1" 
fi 
if [[ "${CDX_TMP}" != "" ]] ; then 
    grep "^${CDX_PROJ}:${CDX_TMP}=" ~/.cdx_data >/dev/null 2>&1 
    if [[ $? != 0 ]] ; then 
      echo "No directory name '${CDX_TMP}' for project '${CDX_PROJ}'." 
      cdxl 
    else 
      export CDX_DIR="${CDX_TMP}" 
      cd $(grep "^${CDX_PROJ}:${CDX_DIR}=" ~/.cdx_data 
       | sed 's/^.*=//' 
       | head -1 
       | sed "s:^~:$HOME:") 
    fi 
fi 
CDX_TMP=$(grep "^${CDX_PROJ}:${CDX_DIR}=" ~/.cdx_data 
    | sed 's/^.*=//' 
    | head -1 
    | sed "s:^~:$HOME:") 
echo "Current project is: [${CDX_PROJ}]" 
echo "Current directory is: [${CDX_DIR}]" 
echo "      [${CDX_TMP}]" 
echo "Actual directory is: [${PWD}]" 
unset CDX_TMP 

Nó sử dụng ba biến môi trường được đặt trước để sử dụng riêng: "CDX_PROJ", "CDX_DIR""CDX_TMP". Khác với các tệp và bí danh đã đề cập ở trên, không có tài nguyên nào khác được sử dụng. Đó là giải pháp đơn giản nhất nhưng dễ thích nghi nhất mà tôi có thể nghĩ ra. May mắn nhất.

3

Khi tôi đã cd vào một thư mục dài như vậy, tôi có điều đó trong lịch sử. Sau đó, tôi chỉ cần nhập Ctrl-R cho lời nhắc "(ngược lại tìm kiếm)" và nhập một vài ký tự, như Dev/C xuất hiện ở đâu đó trong đường dẫn, và nó cho tôi biết lệnh tôi đã phát hành sau đó và tôi có thể dễ dàng chuyển đến nó một lần nữa.

Điều đó hoạt động khá tốt trong thực tế. Bởi vì nó sẽ không tìm thấy một mục nhập nếu bạn chưa gõ đường dẫn đó trong một thời gian, điều đó có nghĩa là làm công việc để làm cho mọi việc trở nên dễ dàng hơn có lẽ sẽ không xứng đáng với thời gian. Nhưng nó chắc chắn sẽ tìm thấy nó nếu bạn sử dụng nó gần đây. Đó là chính xác những gì tôi cần.

Trong một cách nào đó, đó là một bộ nhớ cache tự tổ chức cho các lệnh dài & path-tên :)

+0

Thật không may chỉ hữu ích cho các đường dẫn tuyệt đối hoặc cho các lệnh cd tương đối mà bạn tình cờ đã phát hành từ cùng một vị trí như nơi bạn xảy ra tại thời điểm bạn nhớ lại lệnh cd. – lhunath

+0

Tôi đang ở trong thói quen luôn gõ "cd" trước khi tôi đi bất cứ nơi nào hoàn toàn khác nhau và thường tôi bắt đầu với "cd ~/..." (tôi không thích "../../" đường dẫn kiểu). Vì vậy, đường dẫn hầu như luôn luôn liên quan đến ~ hoặc chính xác anyway. Tất cả phụ thuộc vào thói quen của bạn. Lịch sử sẽ thích nghi với điều đó là điều tốt đẹp –

3

Bạn có thể muốn xem xét sử dụng một kịch bản như this trong .bashrc của bạn. Tôi đã sử dụng nó trên một cơ sở hàng ngày kể từ khi tôi đọc bài đó. Khá đẫm máu hữu ích.

+0

Thật không may, tôi nhận được một số lỗi. Tôi đã đăng nhận xét lên url. –

+0

Ah, vâng, bạn có thể muốn thay thế câu lệnh 'in' bằng 'echo'. ;-) – wzzrd

+0

Cảm ơn bạn! 1+ cho mẹo tuyệt vời :) –

32

Sử dụng liên kết tượng trưng có lẽ là ý tưởng hay nhất; nhưng bạn có thể làm điều đó dễ dàng hơn là bán tất cả chúng vào thư mục chính của bạn.

Như bạn đã đề cập, BASH có một tính năng được gọi là CDPATH thực sự hữu ích ở đây.

Chỉ cần tạo ra một thư mục ẩn trong homedir của bạn (vì vậy nó không lộn xộn homedir của bạn quá nhiều):

$ mkdir ~/.paths 
$ cd ~/.paths 
$ ln -s /my/very/long/path/name/to/my/project project 
$ ln -s /some/other/very/long/path/to/my/backups backups 
$ echo 'CDPATH=~/.paths' >> ~/.bashrc 
$ source ~/.bashrc 

Điều này tạo ra một thư mục trong homedir của bạn được gọi là ".paths" có chứa liên kết tượng trưng cho tất cả các bạn vị trí thư mục dài mà bạn thường xuyên sử dụng, sau đó đặt biến bash CDPATH vào thư mục đó (trong tệp .bashrc của bạn) và đọc lại tệp .bashrc.

Bây giờ, bạn có thể đi đến bất kỳ của những con đường từ bất cứ nơi nào:

$ cd project 
$ cd backups 

Rời bạn với một CDPATH ngắn, không aliasses cluttering, và quan trọng hơn: Một cách rất dễ dàng để điều hướng đến những lâu đường dẫn từ các ứng dụng khác, chẳng hạn như các ứng dụng giao diện người dùng, bằng cách đi vào ~/.path hoặc thêm thư mục đó vào thanh bên của ứng dụng giao diện người dùng của bạn.

Có lẽ là giải pháp toàn diện dễ nhất mà bạn có thể có.

+1

+1 Ý tưởng rất hay để sử dụng các tệp ẩn! –

+1

Wow! Tâm trí tôi bị thổi bay! – sixtyfootersdude

+0

hoàn hảo, cảm ơn bạn! – themarkappleby

3

Người dùng jh đề xuất các lệnh Pushd và Popd. Tôi chia sẻ ở đây một số kịch bản Bash của tôi mà tôi đã tìm thấy trong Unix Power Tools -book. Chúng rất tuyệt khi thư mục của bạn bị quá lâu :)

#Moving fast between directories 
alias pd=pushd 
alias pd2='pushd +2' 
alias pd3='pushd +3' 
alias pd4='pushd +4' 

Lệnh 'pushd + n' "quay" chồng. Lệnh đảo ngược 'popd + n' xóa mục nhập của ngăn xếp. Nếu ngăn xếp của bạn quá dài, hãy sử dụng 'lặp lại popd'. Để kiểm tra, ngăn xếp của bạn dài 12 thư mục:

repeat 11 popd 

Khi bạn muốn xem chồng của mình, hãy viết 'pushd'. Để đọc thêm, tôi giới thiệu cuốn sách ở các trang 625-626.

1

quản lý đòi hỏi cả hai tạo ra nhanh chóng và loại bỏ các thư mục. Tạo nhiều directiories:

mkdir -p user/new_dir/new/_dir/.../new_dir 

Remove đệ quy nhiều thư mục (phải rất cẩn thận khi bạn đang ở trong thư mục thấp hơn!):

rm -r dir/.../new_dir/ 

Để đọc thêm, các cheat sheet có thể giúp bạn:

http://www.scribd.com/doc/2082838/Bash-Command-Line-History-Cheat-Sheet

Tiếp theo ains một số cốm, nhưng tôi thấy nó khá khó đọc. Tôi không thể nhận được lệnh, như Meta +>, làm việc. Họ có thể giúp bạn trong việc điều hướng các thư mục dài.

2

Có nền tảng ý tưởng nổi tiếng, như tạo bí danh:

alias cdfoo="cd /long/path/to/foo" 

và cũng "thả sỏi"

export foo=/long/path/to/foo 

và cũng làm cho trên "dựa trên dự án". Tôi sử dụng thư mục 'ticket based'.

topdir=ticket_12345 
alias cdfoo="cd home/me/sandbox/$topdir/long/path/to/foo" 
export foo="/home/me/sandbox/$topdir/long/path/to/foo" 

nhưng ngoài tất cả điều này, đôi khi bạn chỉ cần chuyển qua lại ở nơi bạn vừa mới sử dụng menu dòng lệnh. (pushd và popd là cồng kềnh, IMHO).

Tôi sử dụng acd_func.sh (được liệt kê bên dưới).Khi đã xác định, bạn có thể làm

cd -

để xem danh sách các thư mục gần đây, với một thực đơn số

cd -2

để đi đến thư mục thứ hai nhất gần đây.

Rất dễ sử dụng, rất tiện dụng.

Dưới đây là các mã:

# Insert into .profile, .bash_profile or wherever 
    # acd_func 1.0.5, 10-nov-2004 
    # petar marinov, http:/geocities.com/h2428, this is public domain 

    cd_func() 

{ 
    local x2 the_new_dir adir index 
    local -i cnt 

    if [[ $1 == "--" ]]; then 
    dirs -v 
    return 0 
    fi 

    the_new_dir=$1 
    [[ -z $1 ]] && the_new_dir=$HOME 

    if [[ ${the_new_dir:0:1} == '-' ]]; then 
    # 
    # Extract dir N from dirs 
    index=${the_new_dir:1} 
    [[ -z $index ]] && index=1 
    adir=$(dirs +$index) 
    [[ -z $adir ]] && return 1 
    the_new_dir=$adir 
    fi 

    # 
    # '~' has to be substituted by ${HOME} 
    [[ ${the_new_dir:0:1} == '~' ]] && the_new_dir="${HOME}${the_new_dir:1}" 

    # 
    # Now change to the new dir and add to the top of the stack 
    pushd "${the_new_dir}" > /dev/null 
    [[ $? -ne 0 ]] && return 1 
    the_new_dir=$(pwd) 

    # 
    # Trim down everything beyond 11th entry 
    popd -n +11 2>/dev/null 1>/dev/null 

    # 
    # Remove any other occurence of this dir, skipping the top of the stack 
    for ((cnt=1; cnt <= 10; cnt++)); do 
    x2=$(dirs +${cnt} 2>/dev/null) 
    [[ $? -ne 0 ]] && return 0 
    [[ ${x2:0:1} == '~' ]] && x2="${HOME}${x2:1}" 
    if [[ "${x2}" == "${the_new_dir}" ]]; then 
     popd -n +$cnt 2>/dev/null 1>/dev/null 
     cnt=cnt-1 
    fi 
    done 

    return 0 
} 

alias cd=cd_func 

if [[ $BASH_VERSION > "2.05a" ]]; then 
    # ctrl+w shows the menu 
    bind -x "\"\C-w\":cd_func -- ;" 
fi 
2

này cũng có thể là một chức năng hữu ích để đưa vào .bashrc của bạn; nó di chuyển lên một hoặc nhiều thư mục, hoặc đến một thư mục có tên, tức là nếu bạn đang ở trong /a/b/c/d/ bạn có thể làm up 3 hoặc up a để kết thúc bằng số a.

Tôi không biết mình đã tìm thấy điều này ở đâu; nếu bạn biết, vui lòng nhận xét hoặc thêm thuộc tính.

function up() 
{ 
    dir="" 
    if [ -z "$1" ]; then 
     dir=.. 
    elif [[ $1 =~ ^[0-9]+$ ]]; then 
     x=0 
     while [ $x -lt ${1:-1} ]; do 
      dir=${dir}../ 
      x=$(($x+1)) 
     done 
    else 
     dir=${PWD%/$1/*}/$1 
    fi 
    cd "$dir"; 
} 
+0

Bạn có chắc chắn nó đúng không? Đối với một số lý do, lệnh, chẳng hạn như "lên 3", không làm bất cứ điều gì với tôi. Thậm chí không có thông báo lỗi. –

+0

+1 Kịch bản tuyệt vời, hoạt động tuyệt vời cho tôi. – iano

2

Nếu bạn muốn chuyển sang zsh, điều này rất easy-- chỉ cần sử dụng "bí danh -g" (bí danh toàn cầu, tức là một bí danh mà làm việc bất cứ nơi nào trong lệnh, không chỉ là từ đầu tiên).

# alias -g c=/my/super/long/dir/name 
# cd c 
# pwd 
/my/super/long/dir/name 

Trong bash, tôi nghĩ rằng điều gần gũi nhất bạn sẽ nhận được phong cách 'răng cưa' là viết một hàm:

function ccd { 
    case "$1" in 
    c) cd /blah/blah/blah/long/path/number/one ;; 
    foo) cd /blah/blah/totally/different path ;; 
    "multiword phrase") cd /tmp ;; 
    esac 
} 

Điều này có nghĩa sử dụng một cái gì đó khác hơn là "cd" như lệnh khi bạn muốn một phím tắt, nhưng khác hơn thế, nó linh hoạt; bạn cũng có thể thêm "ls" vào hàm để nó luôn nhắc bạn những gì có trong thư mục sau cd, v.v.

(Lưu ý sử dụng đối số multiword như trên, bạn cần báo giá nó trên lệnh dòng, như thế này:

ccd "multiword phrase" 

vì vậy nó không thực sự tất cả những gì thuận tiện Nhưng nó sẽ có tác dụng nếu bạn cần)

2

Dựa trên đề nghị Andrew Medico, hãy tìm hiểu J

4

xem xét lại... Hôm nay tôi nhận được liên kết này từ một trang web xã hội đánh dấu, sau đó ngay lập tức tôi nhớ đến câu hỏi này:

Navigation with bm

Chúng tôi tiếp tục đơn giản, đơn giản đánh dấu bằng ký tập tin và sử dụng một công cụ gọi là bm làm việc tra cứu. Công cụ này cũng có thể là được sử dụng để chỉnh sửa chỉ mục dấu trang động như được hiển thị bên dưới, trong đó chúng tôi thêm các thư mục từ ví dụ trước đó vào chỉ mục.

+0

Đây là một lệnh nhỏ tuyệt vời. Cảm ơn vì chỉ ra điều ấy! –

1

Tôi nhận ra câu hỏi là khá cũ, nhưng không có kịch bản nào ở đó hài lòng với tôi, vì vậy tôi đã viết một câu mới.

Dưới đây là các yêu cầu tôi nghĩ trong đầu:

1) Chỉ sử dụng bash lệnh - Tôi có ý định sử dụng này trên nhiều unices khác nhau - Linux, Cygwin, HP-UX, AIX, và một vài người khác, vì vậy tôi không thể phụ thuộc vào grep là phù hợp. May mắn thay tôi có bash ở khắp mọi nơi tôi làm việc.

2) Mã ngắn - Tôi muốn có thể liên kết mã này với khóa trong màn hình GNU và chỉ cần nhấn phím đó để dán tập lệnh vào bash shell hiện tại tôi đang sử dụng, để tôi không phải thiết lập cấu hình bash trên mọi hệ thống tôi sử dụng. Bất cứ điều gì siêu dài sẽ gây phiền nhiễu và mất quá nhiều thời gian để dán.

3) Không sử dụng tệp - Không muốn chia sẻ nhật ký được chia sẻ với các tệp ngẫu nhiên.

4) Hành động giống như "cd" trong trường hợp thông thường. Không muốn phải suy nghĩ về lệnh nào để sử dụng trước khi tôi bắt đầu gõ.

5) Cung cấp "lên" sử dụng như câu trả lời này: How to manage Long Paths in Bash?

6) Giữ một danh sách các thư mục sử dụng gần đây, và chuyển sang gần đây nhất.

Đây là kịch bản:

#Jump History - Isaiah Damron 
function jfind() { 
    lp=${JNHIST//==${PWD}==/==} 
    lp=${lp%%${lp#==*$1*==}} 
    lp=${lp##${lp%==*$1*==*}} 
    lp=${lp//==/} 
    [[ -d "$lp" ]] && echo $lp && return 0 
    return 1; 
} 
function jadd() { 
    [[ -z "$JNHIST" ]] && export JNHIST='==' 
    [[ 3000 -lt ${#JNHIST} ]] && export JNHIST=${JNHIST:0:3000} && export JNHIST="${JNHIST%==*}==" 
    export JNHIST="==$PWD${JNHIST//==${PWD}==/==}" 
} 
function j() { 
    { cd $* 2> /dev/null && jadd; } \ 
    || { cd ${PWD/$1*/}$1 2> /dev/null && jadd; } \ 
    || { jfind $1 \ 
     && { cd $(jfind $1 ) 2> /dev/null && jadd; } ; } \ 
    || cd $* 
} 
function jh() { 
    [[ -z "$1" ]] && echo -e ${JNHIST//==/\\n} 
    [[ -n "$1" ]] && jfind $1 && cd $(jfind $1) && jadd 
} 

Cách sử dụng:

jh [parameters] 

Nếu gọi là ngày của riêng mình, mà không cần bất kỳ thông số, nó sẽ tạo ra danh sách lịch sử hiện nay. Nếu nó có một tham số, sau đó nó tìm kiếm thông qua lịch sử cho thư mục được sử dụng gần đây nhất có chứa chuỗi $ 1, và cd của nó.

j {parameters} 

cd parameters. Nếu điều đó không thành công, nó sẽ kiểm tra xem có bất kỳ thư mục cha nào của $ PWD khớp với $ 1, và cd của nó không. Nếu điều đó không thành công, sau đó nó gọi jh $1. Nếu điều đó không thành công, thì kết quả sẽ xuất ra kết quả của cd parameters

Lưu ý: Tôi đã sử dụng '==' làm dấu phân cách nội bộ. Hy vọng rằng bạn không có bất kỳ thư mục có chứa một '==', nhưng nếu bạn làm bạn sẽ phải thay đổi xung quanh kịch bản. Chỉ:% s/==/whatever/g

Các vấn đề liên quan