참고 :이 불변 인 고체, 휴대용, 기성품 솔루션, 생각하는 긴 바로 그 이유.
아래는 완전한 POSIX 호환 스크립트 / 함수 이므로 크로스 플랫폼입니다 (macOS에서도 작동 readlink
하지만 여전히 -f
10.12 (Sierra)를 지원하지 않음 ). POSIX 쉘 언어 기능 만 사용하고 POSIX 호환 유틸리티 호출 만 사용합니다. .
그것은이다 GNU의 휴대용 구현readlink -e
(의 엄격한 버전 readlink -f
).
당신은 할 수 있습니다 실행 스크립트 와를sh
또는 소스 기능 에 bash
, ksh
그리고zsh
:
예를 들어, 스크립트 내에서 다음과 같이 스크립트를 사용하여 심볼릭 링크가 해결 된 실행중인 스크립트의 실제 디렉토리를 얻을 수 있습니다.
trueScriptDir=$(dirname -- "$(rreadlink "$0")")
rreadlink
스크립트 / 함수 정의 :
코드는 이 답변 에서 감사의 말로 채택되었습니다 . Node.js가 설치되어있는 경우 여기 에 설치할 수있는 독립 실행 형 유틸리티 버전
도 만들었습니다 .bash
npm install rreadlink -g
#!/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 /var@ -> /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 "$@"
보안 접선 :
jarno 는 내장 command
이 동일한 이름의 별명 또는 쉘 함수로 음영 처리되지 않도록하는 함수와 관련하여 주석을 묻습니다.
어떤 경우 unalias
또는 unset
및 [
별칭 또는 쉘 함수로 설정?
원래 의미를 rreadlink
갖도록 하는 동기 command
는 재정의와 같은 대화식 쉘에서 표준 명령을 음영 처리하는 데 사용되는 편의 별칭 및 기능 을 우회 하는 데 사용하는 것입니다.ls
는 선호하는 옵션을 포함하도록 하는 것입니다.
나는 그것이 걱정, 신뢰할 수없는, 악의적 인 환경과 당신 않는있는 거 거래 말을하는 것이 안전하다고 생각 unalias
하거나 unset
, 또는 그 문제에 대해, - while
, do
... - 재정의되는 문제가되지 않습니다.
이 일 함수가 원래의 의미와 동작을하기에 의존해야하는가 - 그 주위에 방법이 없습니다.
POSIX와 같은 쉘은 내장의 재정의를 허용하며 언어 키워드조차도 본질적 으로 보안 위험이 있습니다 (그리고 편집증 코드를 작성하는 것은 일반적으로 어렵습니다).
특별히 우려 사항을 해결하려면 :
이 함수에 의존 unalias
하고 unset
원래의 의미를 가지고. 동작을 변경하는 방식으로 쉘 함수 로 재정의 하면 문제가됩니다. 명령 이름 (예 :)을 인용 (일부 )하여 별칭을 우회 하기 때문에 별칭으로 재정의하는 것이 반드시 걱정되는 것은 아닙니다 .\unalias
그러나 인용은 쉘의 옵션 이 아닙니다 . 키워드 ( while
, for
, if
, do
, ...)와 쉘 키워드는 쉘을 통해보다 우선 할 때 기능 에, bash
그리고 zsh
별명은 그래서 당신은 실행해야합니다 쉘 키워드 재정의 방지하려면, 가장 높은 우선 순위를 가지고 unalias
와 이름 (에 있지만, 대화 형이 아닌 bash
같은 스크립트와 같은 쉘 () 별명이되어 있지 기본적으로 확장 - 경우에만 shopt -s expand_aliases
명시 적 처음이라고합니다).
되도록하려면 unalias
- 내장 명령으로 - 원래의 의미를 가지고, 당신은 사용해야 \unset
즉 요구하는, 처음에 unset
: 원래의 의미가
unset
는 내장 쉘 이므로, 그렇게 호출되도록하려면, 그 자체가 함수 로 재정의되지 않아야 합니다 . 인용 부호를 사용하여 별명 양식을 생략 할 수 있지만 쉘 기능 양식-catch 22를 생략 할 수 없습니다.
따라서 unset
내가 말할 수있는 것에서 원래 의미를 가질 수 없다면 모든 악의적 인 재정의에 대해 방어 할 수있는 보장 된 방법이 없습니다.