Примечания: Я считаю, что это быть твердым, портативным, готовым решением, которое неизменно длительного по той же причине.
Ниже полностью POSIX-совместимый скрипт/функция что поэтому кросс-платформенный (работает на MacOS тоже, чьи readlink
еще не поддерживает -f
по состоянию на 10.12 (Sierra)) - он использует только POSIX shell language features и только вызовы утилиты, совместимые с POSIX.
Это переносимая реализация readlink -e
(более строгой версии readlink -f
) ГНУ.
Вы можете запустить сценарий с sh
или источника, функции в bash
, ksh
и zsh
:
Например, внутри сценария вы можете использовать его как следует, чтобы получить сценарий запуска подлинного каталога происхождения, с символическими ссылками:
trueScriptDir=$(dirname -- "$(rreadlink "$0")")
rreadlink
сценарий определение/функция:
код был адаптирован с благодарностью от this answer.
Я также создал автономную служебную версию bash
here, которую вы можете установить с помощью
npm install rreadlink -g
, если у вас установлен 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]"
Касательная на безопасность:
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
является оболочка встроенной, с тем чтобы обеспечить что он вызывается как таковой, вам нужно убедиться, что он сам не переопределяется как функция . Хотя вы можете обойти форму псевдонима с цитированием, вы не можете обойти форму-формулу уловки 22.
Таким образом, если вы не можете положиться на unset
, чтобы иметь свое первоначальное значение, из того, что я могу сказать, нет гарантированного способ защиты от всех вредоносных переопределений.
Это просто работает для (текущего) каталога? Если целью является файл, он ничего не даст ... – dala 2010-04-28 09:18:31
Не работает, если ссылка сломана, поскольку вы не можете сделать ее текущим путем. – 2011-11-16 20:17:47
Подводя итог с учетом ретроспективного анализа: этот ответ работает только в очень ограниченных обстоятельствах , а именно, если символическая ссылка представляет собой _directory_, которая фактически существует _; плюс, вы должны сначала «cd», прежде чем вызывать `pwd -P`. Другими словами: он не позволит вам разрешать (видеть цель) символические ссылки _to files_ или символические ссылки _broken_, а для устранения символических ссылок существующего каталога вам нужно выполнить дополнительную работу (восстановить предыдущий рабочий каталог или локализовать ` cd` и `pwd -P` вызывает в подоболочке). – mklement0 2015-10-22 20:56:55