У меня есть скрипт, который зависит от некоторых переменных среды, некоторые из которых являются путями

Сценарий проверяет, выполняется ли он в правильном каталоге, проверяя $(pwd)/expected-subdir отношению к $VARIABLE_PATH/expected_subdir

Конечно, если $VARIABLE_PATH == $(pwd)/ (я естественно добавил косую черту в конец переменной при вводе), совпадение строки не выполняется, даже если каталоги технически исправны.

Есть ли способ однозначно идентифицировать каталог с указанием пути к нему и посмотреть, будет ли другой путь направлять вас в то же место? Возможно, указанный путь не является нормальным, поэтому эту проверку все же следует выполнить, но, по-видимому, нет необходимости заставлять путь точно совпадать, когда случайная косая черта или даже относительный каталог (я знаю, что иногда это может быть плохо) направит вас в том же месте.

2 ответа2

1

Вы можете сравнить их номера инодов, которые можно получить из ls -i

ls -di path/to/dir

(-d позволяет ls показывать идентификаторы inode для содержимого каталога);

или из stat

stat path/to/dir | grep -o 'Inode:\ [0-9]*' | cut -d' ' -f2

Вы также можете использовать readlink -f чтобы получить полный путь к каталогу.

1

Этот другой ответ упоминает 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 . Это зависит от того, какие аспекты путей важны для вас. Основные отличия:

  1. Если /foo/bar/ является привязкой к /moo/baz/ то stat --format=%d:%i скажу вам, что они одинаковые, но readlink или realpath будут считать их разными.
  2. Ситуация осложняется с символическими ссылками и .. Смотрите 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 .

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .