
Я ищу команду, которая вернет абсолютный путь к файлу, без разрешения символических ссылок. В общем, realpath
делает это хорошо.
$ mkdir /tmp/test; cd /tmp/test
$ mkdir foo
$ ln -s foo bar
$ realpath -se bar # good, this does not resolve the symlink
/tmp/test/bar
Это также работает для файлов внутри каталогов символических ссылок.
$ touch foo/file
$ realpath -se bar/file # good, this does not resolve the symlink
/tmp/test/bar/file
Однако это не удается, когда нынешний директорявляетсякаталог символической ссылки
$ cd bar
$ pwd
/tmp/test/bar
$ realpath -se file # this fails, returning the target
/tmp/test/foo/file
$ realpath -se . # this also fails, returning the target
/tmp/test/foo
$ realpath -se /tmp/test/bar/file # yet this works
/tmp/test/bar/file
$ realpath -se /tmp/test/bar # and this works
/tmp/test/bar
Почему realpath
так происходит? (Это ошибка?) Есть ли способ realpath
никогда не разрешать символическую ссылку или есть другой метод, который мне следует использовать?
решение1
Текущий рабочий каталог (CWD) процесса наследуется на уровне ОС от предыдущего процесса или может быть изменен для текущего процесса с помощью chdir(2)
. Операционная система (здесь я имею в виду «ядро»), конечно, всегда разрешит любую символическую ссылку, чтобы определить конечный результат, который должен быть каталогом, а не символической ссылкой (на каталог). Например, предыдущий системный вызов ( chdir(2)
) может вернуть ошибку ELOOP
, когда было слишком много символических ссылок для разрешения. Таким образом, с точки зрения ОС, не может быть CWD, не являющегося каталогом для какого-либо процесса: ОС всегда разрешит его как реальный путь без какой-либо символической ссылки где-либо.
После того, как оболочка выполнила cd /tmp/test/bar
, путь CWD был разрешен ОС в /tmp/test/foo
. Например, в системе Linux ls -l /proc/$$/cwd
будет показана ссылка на разрешенный путь, видимый ядром: /tmp/test/foo
.
Тот факт, что оболочка все еще отображается bar
в приглашении, объясняется тем, что она помнитCDкоманда выполнена ранее. Поведение может зависеть от типа оболочки. Я предполагаю, что это bash. Так что это встроенная pwd
(но не внешняя /bin/pwd
команда), $PWD
переменная и ее использование $PS1
будут «лгать» пользователю о текущем каталоге.
Любой процесс, например realpath
, или /bin/pwd
запущенный из оболочки, конечно же унаследуетдействительныйCWD, который есть /tmp/test/foo
. Так что это не ошибка в realpath
, у него никогда не будет конкретной информации о bar
.
Один из возможных неудобных способов, предложенный Кусаланандой, — это каким-то образом повторно использовать $PWD
переменную и добавлять ее к realpath
аргументу только в том случае, если ее аргумент еще не является абсолютным.
Вот пример. Я не уверен, что нет способов злоупотребить этим. Например, хотя функция ниже справилась бы, $PWD
сама переменная ведет себя не очень хорошо в bash 4.4.12 (Debian 9), но отлично работает в bash 5.0.3 (Debian 10), если в пути есть символ перевода строки. Когда где-то есть символ перевода строки, для того, чтобы быть полезным, -z
также следует добавить опцию, realpath
но я не собираюсь переписывать весь разбор опций в этом простом примере.
myrealpathnofollowsym () {
for p in "$@"; do
if ! printf '%s' "$p" | grep -q -- '^/'; then
realpath -se "$PWD/$p"
else
realpath -se "$p"
fi
done
}