Miễn là bạn có quyền thực thi trên thư mục hiện tại - hoặc trên thư mục mà bạn đã thực thi tập lệnh shell của mình - nếu bạn muốn một đường dẫn tuyệt đối đến thư mục, tất cả những gì bạn cần là cd.
Bước 10 của cdthông số kỹ thuật
Nếu -Ptùy chọn có hiệu lực, $PWDbiến môi trường sẽ được đặt thành chuỗi sẽ được xuất theo pwd -P. Nếu không có đủ quyền trên thư mục mới hoặc trên bất kỳ cha mẹ nào của thư mục đó, để xác định thư mục làm việc hiện tại, giá trị của $PWDbiến môi trường là không xác định.
Và hơn thế nữa pwd -P
Tên đường dẫn được ghi vào đầu ra tiêu chuẩn sẽ không chứa bất kỳ thành phần nào đề cập đến các tệp có liên kết tượng trưng loại. Nếu có nhiều tên đường dẫn mà pwdtiện ích có thể ghi vào đầu ra tiêu chuẩn, một bắt đầu bằng một ký tự / dấu gạch chéo và một hoặc nhiều bắt đầu bằng hai / ký tự gạch chéo, thì nó sẽ viết tên đường dẫn bắt đầu bằng một ký tự gạch chéo / đơn. Tên đường dẫn không được chứa bất kỳ ký tự / dấu gạch chéo nào sau một hoặc hai ký tự gạch chéo.
Đó là bởi vì cd -Pcó để thiết lập thư mục làm việc hiện tại với những gì pwd -Pkhác nên in và rằng cd -có in $OLDPWDrằng các công việc sau:
mkdir ./dir
ln -s ./dir ./ln
cd ./ln ; cd . ; cd -
ĐẦU RA
/home/mikeserv/test/ln
chờ đã ...
cd -P . ; cd . ; cd -
ĐẦU RA
/home/mikeserv/test/dir
Và khi tôi in với cd -tôi đang in $OLDPWD. cdthiết lập $PWDngay khi tôi cd -P . $PWDbây giờ là một đường dẫn tuyệt đối /- vì vậy tôi không cần bất kỳ biến nào khác. Và trên thực tế, tôi nên thậm chí không cần các dấu .nhưng có một hành vi cụ thể của thiết lập lại $PWDđể $HOMEtrong một vỏ tương tác khi cdlà không trang trí. Vì vậy, nó chỉ là một thói quen tốt để phát triển.
Vì vậy, chỉ cần thực hiện các thao tác trên trên đường dẫn trong ${0%/*}là quá đủ để xác minh $0đường dẫn, nhưng trong trường hợp $0chính nó là một liên kết mềm, có lẽ bạn không thể thay đổi thư mục vào đó.
Đây là một chức năng sẽ xử lý rằng:
zpath() { cd -P . || return
_out() { printf "%s$_zdlm\n" "$PWD/${1##*/}"; }
_cd() { cd -P "$1" ; } >/dev/null 2>&1
while [ $# -gt 0 ] && _cd .
do if _cd "$1"
then _out
elif ! [ -L "$1" ] && [ -e "$1" ]
then _cd "${1%/*}"; _out "$1"
elif [ -L "$1" ]
then ( while set -- "${1%?/}"; _cd "${1%/*}"; [ -L "${1##*/}" ]
do set " $1" "$(_cd -; ls -nd -- "$1"; echo /)"
set -- "${2#*"$1" -> }"
done; _out "$1"
); else ( PS4=ERR:\ NO_SUCH_PATH; set -x; : "$1" )
fi; _cd -; shift; done
unset -f _out _cd; unset -v _zdlm
}
Nó cố gắng làm nhiều nhất có thể trong shell hiện tại - mà không cần gọi một subshell - mặc dù có các subshell được gọi cho các lỗi và các liên kết mềm không trỏ đến các thư mục. Nó phụ thuộc vào lớp vỏ tương thích POSIX và tương thích POSIX lscũng như _function()không gian tên sạch . Nó vẫn sẽ hoạt động tốt mà không cần cái sau, mặc dù nó có thể ghi đè lên unsetmột số hàm shell hiện tại trong trường hợp đó. Nhìn chung, tất cả các phụ thuộc này đều có sẵn trên máy Unix.
Được gọi có hoặc không có đối số, điều đầu tiên nó được đặt lại $PWDthành giá trị chính tắc của nó - nó giải quyết bất kỳ liên kết nào trong đó đến các mục tiêu của chúng khi cần thiết. Được gọi mà không có đối số và đó là về nó; nhưng được gọi với họ và nó sẽ giải quyết và chuẩn hóa đường dẫn cho mỗi hoặc nếu không thì in một tin nhắn stderrtại sao không.
Bởi vì nó chủ yếu hoạt động trong trình bao hiện tại nên nó có thể xử lý một danh sách đối số có độ dài bất kỳ. Nó cũng tìm kiếm $_zdlmbiến số (cũng unsetlà khi nó thông qua) và in giá trị thoát C của nó ngay bên phải mỗi đối số của nó, mỗi đối số luôn được theo sau bởi một \nký tự ewline.
Nó thực hiện rất nhiều thay đổi thư mục, nhưng, ngoài việc đặt nó thành giá trị chính tắc của nó, nó không ảnh hưởng $PWD, mặc dù $OLDPWDkhông thể bằng bất kỳ phương tiện nào được tính khi nó đi qua.
Nó cố gắng bỏ từng đối số của nó ngay khi có thể. Đầu tiên nó cố gắng cdvào $1. Nếu nó có thể in đường dẫn chính tắc của đối số tới stdout. Nếu nó không thể kiểm tra nó $1tồn tại và không phải là một liên kết mềm. Nếu đúng, nó in.
Theo cách này, nó xử lý bất kỳ đối số loại tệp nào mà shell có quyền truy cập trừ khi $1là một liên kết tượng trưng không trỏ đến một thư mục. Trong trường hợp đó, nó gọi whilevòng lặp trong một subshell.
Nó gọi lsđể đọc liên kết. Thư mục hiện tại phải được thay đổi thành giá trị ban đầu trước tiên để xử lý một cách đáng tin cậy bất kỳ đường dẫn tham chiếu nào và do đó, trong lệnh thay thế lệnh con, hàm này thực hiện:
cd -...ls...echo /
Nó thoát khỏi bên trái của lsđầu ra ít nhất là phải chứa đầy đủ tên và chuỗi của liên kết ->. Lúc đầu tôi đã cố gắng tránh làm điều này với shiftvà $IFShóa ra đây là phương pháp đáng tin cậy nhất gần như tôi có thể hình dung. Đây là điều tương tự poor_mans_readlink của Gilles - và nó được thực hiện tốt.
Nó sẽ lặp lại quá trình này trong một vòng lặp cho đến khi tên tệp được trả về lschắc chắn không phải là một liên kết mềm. Tại thời điểm đó, nó chuẩn hóa đường dẫn đó như trước với cdsau đó in.
Ví dụ sử dụng:
zpath \
/tmp/script \ #symlink to $HOME/test/dir/script.sh
ln \ #symlink to ./dir/
ln/nl \ #symlink to ../..
/dev/fd/0 \ #currently a here-document like : dash <<\HD
/dev/fd/1 \ #(zlink) | dash
file \ #regular file
doesntexist \ #doesnt exist
/dev/disk/by-path/pci-0000:00:16.2-usb-0:3:1.0-scsi-0:0:0:0 \
/dev/./././././././null \
. ..
ĐẦU RA
/home/mikeserv/test/dir/script.sh
/home/mikeserv/test/dir/
/home/mikeserv/test/
/tmp/zshtpKRVx (deleted)
/proc/17420/fd/pipe:[1782312]
/home/mikeserv/test/file
ERR: NO_SUCH_PATH: doesntexist
/dev/sdd
/dev/null
/home/mikeserv/test/
/home/mikeserv/
Hoặc có thể ...
ls
dir/ file file? folder/ link@ ln@ script* script3@ script4@
zdlm=\\0 zpath * | cat -A
ĐẦU RA
/home/mikeserv/test/dir/^@$
/home/mikeserv/test/file^@$
/home/mikeserv/test/file$
^@$
/home/mikeserv/test/folder/^@$
/home/mikeserv/test/file$ #'link' -> 'file\n'
^@$
/home/mikeserv/test/dir/^@$ #'ln' -> './dir'
/home/mikeserv/test/script^@$
/home/mikeserv/test/dir/script.sh^@$ #'script3' -> './dir/script.sh'
/home/mikeserv/test/dir/script.sh^@$ #'script4' -> '/tmp/script' -> ...