Этот другой ответ упоминает stat
. Давайте улучшим подход:
a="$(stat --format=%d:%i -- "$(pwd)/expected-subdir/")" || printf "Error A\n" >&2
b="$(stat --format=%d:%i -- "$VARIABLE_PATH/expected-subdir/")" || printf "Error B\n" >&2
[ "$a" = "$b" ] || printf "Not the same path.\n" >&2
Заметки:
- Мы используем
stat --format=
, поэтому дальнейший анализ не требуется.
%d
- номер устройства, %i
- номер индекса. Одного последнего недостаточно, потому что два объекта на разных устройствах (файловых системах) могут иметь один и тот же номер инода.
- Оболочка достаточно умна, чтобы отдельно обрабатывать кавычки внутри и снаружи
$( )
.
printf
-s - просто заглушки для обработки ошибок. В реальном сценарии вы, вероятно, захотите что-то вроде
[ "$a" = "$b" ] || { printf "Not the same path.\n" >&2; exit 2; }
Несуществующий путь заставит stat
выдать ошибку.
- Конечные косые черты важны. Если
foo
является символической ссылкой на каталог, stat foo
будет проверять символическую ссылку, а stat foo/
будет проверять каталог. Альтернативно stat -L
и используйте это возможно.
--
в случае, если $(pwd)
или $VARIABLE_PATH
начинаются с -
(см. правило 10 здесь ).
Есть утилиты readlink -f
(также упомянутые в другом ответе) и утилиты realpath
. Один из них может быть лучшим подходом, чем stat
. Это зависит от того, какие аспекты путей важны для вас. Основные отличия:
- Если
/foo/bar/
является привязкой к /moo/baz/
то stat --format=%d:%i
скажу вам, что они одинаковые, но readlink
или realpath
будут считать их разными.
- Ситуация осложняется с символическими ссылками и
..
Смотрите man 1 realpath
, особенно параметры -L
и -P
.
При использовании символических ссылок, если ваш скрипт изменяет каталог вверх (в сторону /
, например, cd ..
), вы можете получить разные результаты в зависимости от того, с чего начинаете, даже если stat
или readlink -f
говорят, что две начальные точки совпадают (сравните Почему ls ..
показывает реальный родительский контент, когда я нахожусь в каталоге символических ссылок?). Рассмотрим этот подход:
# early in the script
set -P
cd . # seems like no-op but updates the PWD variable to physical path
При использовании bind mounts, если ваш скрипт меняет каталог, вы можете получить разные результаты в зависимости от того, с чего начинаете, даже если stat
говорит, что две начальные точки совпадают. Рассмотрим пример с /foo/bar/
и /moo/baz/
(уже представлен выше). Очевидно, что /foo/
может полностью отличаться от /moo/
. Но также /foo/bar/abc
может отличаться от /moo/baz/abc
потому что любой abc
может быть независимой привязкой к чему-то другому. Так что не только cd ..
может поставить вас в другое место, но и cd abc
.
Ну, abc
может быть файлом. Что делать, если вы связываете другой файл с /moo/baz/abc
но не с /foo/bar/abc
? Воспринимаемые файлы будут отличаться, даже если stat
говорит, что вы в одном месте!
Из-за этих проблем вы можете предпочесть readlink
или realpath
stat
.
stat
, readlink
и realpath
не требуются POSIX. В вашем случае портативное решение может выглядеть так:
a="$(cd -P -- "expected-subdir" && pwd -P)" || exit 1
b="$(cd -P -- "$VARIABLE_PATH/expected-subdir" && pwd -P)" || exit 1
[ "$a" = "$b" ] || { printf "Not the same path.\n" >&2; exit 2; }
Он работает с помощью cd
-ing к любому пути и извлекает его с помощью pwd
. Эти операции явно вынуждены действовать "физически" (-P
).
set -P
не является POSIX. Если вы хотите, чтобы оболочка POSIX "эмулировала" set -P
, замените cd
и pwd
:
cd() { command cd -P "$@"; }
pwd() { command pwd -P "$@"; }
POSIX требует, чтобы последний параметр -P
или -L
вступил в силу, поэтому pwd -L
прежнему извлекает логический путь, даже если функция "внедряет" -P
; то же самое для cd -L
.