Lưu ý: Tôi tin rằng đây là một, di động, làm sẵn giải pháp vững chắc, đó là luôn dài cho rằng lý do rất.
Dưới đây là một kịch bản hoàn toàn POSIX-compliant/chức năng đó là do cross-platform (hoạt động trên hệ điều hành MacOS quá, mà readlink
vẫn không hỗ trợ -f
lúc 10.12 (Sierra)) - nó chỉ sử dụng POSIX shell language features và chỉ các cuộc gọi tiện ích tuân thủ POSIX.
Đây là triển khai di động của GNU readlink -e
(phiên bản chặt chẽ của readlink -f
).
Bạn có thể chạy kịch bản với sh
hoặc nguồn chức năng trong bash
, ksh
, và zsh
:
Ví dụ, bên trong một kịch bản mà bạn có thể sử dụng nó như sau để có được thư mục gốc thực sự của tập lệnh chạy, với các liên kết tượng trưng được giải quyết:
trueScriptDir=$(dirname -- "$(rreadlink "$0")")
rreadlink
script/chức năng định nghĩa:
Mã này đã được chuyển thể với lòng biết ơn từ this answer.
Tôi cũng đã tạo một phiên bản tiện ích độc lập bash
dựa trên here, mà bạn có thể cài đặt với
npm install rreadlink -g
, nếu bạn đã cài đặt Node.js.
#!/bin/sh
# SYNOPSIS
# rreadlink <fileOrDirPath>
# DESCRIPTION
# Resolves <fileOrDirPath> to its ultimate target, if it is a symlink, and
# prints its canonical path. If it is not a symlink, its own canonical path
# is printed.
# A broken symlink causes an error that reports the non-existent target.
# LIMITATIONS
# - Won't work with filenames with embedded newlines or filenames containing
# the string ' -> '.
# COMPATIBILITY
# This is a fully POSIX-compliant implementation of what GNU readlink's
# -e option does.
# EXAMPLE
# In a shell script, use the following to get that script's true directory of origin:
# trueScriptDir=$(dirname -- "$(rreadlink "$0")")
rreadlink() (# Execute the function in a *subshell* to localize variables and the effect of `cd`.
target=$1 fname= targetDir= CDPATH=
# Try to make the execution environment as predictable as possible:
# All commands below are invoked via `command`, so we must make sure that
# `command` itself is not redefined as an alias or shell function.
# (Note that command is too inconsistent across shells, so we don't use it.)
# `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not
# even have an external utility version of it (e.g, Ubuntu).
# `command` bypasses aliases and shell functions and also finds builtins
# in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for
# that to happen.
{ \unalias command; \unset -f command; } >/dev/null 2>&1
[ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.
while :; do # Resolve potential symlinks until the ultimate target is found.
[ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
fname=$(command basename -- "$target") # Extract filename.
[ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
if [ -L "$fname" ]; then
# Extract [next] target path, which may be defined
# *relative* to the symlink's own directory.
# Note: We parse `ls -l` output to find the symlink target
# which is the only POSIX-compliant, albeit somewhat fragile, way.
target=$(command ls -l "$fname")
target=${target#* -> }
continue # Resolve [next] symlink target.
fi
break # Ultimate target reached.
done
targetDir=$(command pwd -P) # Get canonical dir. path
# Output the ultimate target's canonical path.
# Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
if [ "$fname" = '.' ]; then
command printf '%s\n' "${targetDir%/}"
elif [ "$fname" = '..' ]; then
# Caveat: something like /var/.. will resolve to /private (assuming /[email protected] -> /private/var), i.e. the '..' is applied
# AFTER canonicalization.
command printf '%s\n' "$(command dirname -- "${targetDir}")"
else
command printf '%s\n' "${targetDir%/}/$fname"
fi
)
rreadlink "[email protected]"
Một tiếp tuyến về an ninh:
jarno, khi đề cập đến chức năng đảm bảo rằng BUILTIN command
không lu mờ bởi một bí danh hoặc vỏ chức năng cùng tên, yêu cầu trong một nhận xét:
Điều gì xảy ra nếu unalias
hoặc unset
và [
được đặt làm bí danh hoặc hàm vỏ?
Động cơ đằng sau rreadlink
đảm bảo rằng command
có ý nghĩa ban đầu của nó là sử dụng nó để bỏ qua (lành tính) tiện bí danh và chức năng thường được sử dụng để shadow lệnh tiêu chuẩn trong vỏ tương tác, chẳng hạn như xác định lại ls
bao gồm tùy chọn ưa thích .
Tôi nghĩ đó là an toàn để nói rằng trừ khi bạn đang làm việc với một môi trường tin cậy, độc hại, lo lắng về unalias
hoặc unset
- hoặc, cho rằng vấn đề, while
, do
... - được định nghĩa lại không phải là một mối quan tâm.
Có một cái gì đó rằng chức năng phải dựa vào để có ý nghĩa và hành vi ban đầu của nó - không có cách nào xung quanh điều đó.
Vỏ giống POSIX này cho phép định nghĩa lại nội trang và thậm chí từ khóa ngôn ngữ là vốn dĩ là nguy cơ bảo mật (và viết mã hoang tưởng nói chung là khó).
Để giải quyết mối quan tâm của bạn đặc biệt:
Chức năng dựa vào unalias
và unset
có ý nghĩa ban đầu của họ. Có chúng được định nghĩa lại là các hàm vỏ theo cách làm thay đổi hành vi của chúng sẽ là một vấn đề; định nghĩa lại là một bí danh là không nhất thiết phải là mối quan ngại, bởi vì trích dẫn (một phần) tên lệnh (ví dụ: \unalias
) bỏ qua bí danh.
Tuy nhiên, trích dẫn là không một lựa chọn cho vỏ từ khóa (while
, for
, if
, do
, ...) và trong khi từ khóa vỏ làm được ưu tiên hơn vỏ chức năng, trong bash
và zsh
bí danh có ưu tiên cao nhất, do đó, để bảo vệ chống lại định nghĩa lại từ khóa shell, bạn phải chạy unalias
với tên của chúng (mặc dù trong không tương tácbash
vỏ (chẳng hạn như tập lệnh) bí danh là không mở rộng theo mặc định - chỉ khi shopt -s expand_aliases
được gọi trước tiên).
Để đảm bảo rằng unalias
- như một dựng sẵn - có ý nghĩa ban đầu của nó, bạn phải sử dụng \unset
vào nó đầu tiên, đòi hỏi rằng unset
có ý nghĩa ban đầu của nó:
unset
là một vỏ BUILTIN, vì vậy để đảm bảo rằng nó được gọi như vậy, bạn phải đảm bảo rằng chính nó không được định nghĩa lại là một hàm . Trong khi bạn có thể bỏ qua một biểu mẫu bí danh có trích dẫn, bạn không thể bỏ qua một biểu mẫu hàm vỏ - bắt 22.
Do đó, trừ khi bạn có thể dựa vào ý nghĩa ban đầu của nó, từ những gì tôi có thể nói, không có bảo đảm cách để bảo vệ chống lại tất cả các định nghĩa lại độc hại.
này chỉ làm việc cho thư mục (hiện tại)? Nếu mục tiêu là một tập tin, nó sẽ không cung cấp bất cứ điều gì ... – dala
Không hoạt động nếu liên kết bị hỏng vì bạn không thể biến nó thành đường dẫn hiện tại của bạn –
Tóm lại với lợi ích của sự nhận thức muộn: Câu trả lời này chỉ hoạt động trong những trường hợp rất hạn chế , cụ thể là nếu liên kết tượng trưng quan tâm đến _directory_ thực sự là _exists_; Ngoài ra, bạn phải 'cd' cho nó trước, trước khi gọi' pwd -P'. Nói cách khác: nó sẽ không cho phép bạn giải quyết (xem mục tiêu) symlink _to files_ hoặc của các liên kết tượng trưng _broken_, và để giải quyết các liên kết thư mục hiện có, bạn phải thực hiện thêm công việc (khôi phục lại thư mục làm việc trước đó hoặc bản địa hoá ' cd' và 'pwd -P' cuộc gọi trong một subshell). – mklement0