Вот команда, которая работает под bash или zsh и выполняется ли она автономно или из источника:
[ -n "$ZSH_VERSION" ] && this_dir=$(dirname "${(%):-%x}") \
|| this_dir=$(dirname "${BASH_SOURCE[0]:-$0}")
Как это работает
Текущее расширение файла zsh: ${(%):-%x}
${(%):-%x}
в zsh раскрывает путь к текущему исполняемому файлу.
Резервный оператор подстановки :-
Вы уже знаете, что ${...}
подставляет переменные внутри строк. Вы можете не знать, что некоторые операции возможны (как в bash , так и zsh ) с переменными во время подстановки, например, в качестве оператора аварийного расширения :-
:
% x=ok
% echo "${x}"
ok
% echo "${x:-fallback}"
ok
% x=
% echo "${x:-fallback}"
fallback
% y=yvalue
% echo "${x:-$y}"
yvalue
Код подсказки %x
Далее мы введем быстрые коды перехода, функция только для zsh. В zsh %x
будет расширяться до пути к файлу, но обычно это происходит только при расширении для строк приглашения . Чтобы включить эти коды в нашу замену, мы можем добавить флаг (%)
перед именем переменной:
% cat apath/test.sh
fpath=%x
echo "${(%)fpath}"
% source apath/test.sh
apath/test.sh
% cd apath
% source test.sh
test.sh
маловероятное совпадение: процент побега и запасной вариант
То, что у нас есть, работает, но было бы лучше избегать создания дополнительной переменной fpath
. Вместо ввода %x
в fpath
мы можем использовать :-
и поставить %x
в резервной строке:
% cat test.sh
echo "${(%):-%x}"
% source test.sh
test.sh
Обратите внимание, что обычно мы помещаем имя переменной между (%)
и :-
, но мы оставляем это поле пустым. Переменная с пустым именем не может быть объявлена или установлена, поэтому запасной вариант всегда срабатывает.
Завершение: как насчет print -P %x
?
Теперь у нас почти есть каталог нашего скрипта. Мы могли бы использовать print -P %x
, чтобы получить тот же путь к файлу с меньшим количеством хаков, но в нашем случае, когда нам нужно передать его в качестве аргумента dirname
, для этого потребовались бы дополнительные затраты на запуск новой подоболочки:
% cat apath/test.sh
dirname "$(print -P %x)" # $(...) runs a command in a new process
dirname "${(%):-%x}"
% source apath/test.sh
apath
apath
Оказывается, что хакерский способ более эффективен и лаконичен.