В наиболее распространенных случаях $0будет содержать путь, абсолютный или относительный к сценарию, поэтому
script_path=$(readlink -e -- "$0")
(при условии, что readlinkкоманда есть и она поддерживается -e), как правило, является достаточно хорошим способом получения канонического абсолютного пути к сценарию.
$0 присваивается из аргумента, определяющего скрипт как переданный интерпретатору.
Например, в:
the-shell -shell-options the/script its args
$0получает the/script.
Когда вы бежите:
the/script its args
Ваша оболочка сделает:
exec("the/script", ["the/script", "its", "args"])
Если в сценарии, например, есть #! /bin/sh -она, система преобразует это в:
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "the/script", "its", "args"])
(если он не содержит ничего, или, в более общем случае, если система возвращает ошибку ENOEXEC, то ваша оболочка будет делать то же самое)
Существует исключение для сценариев setuid / setgid на некоторых системах, где система открывает сценарий на некоторых fd xи запускает вместо этого:
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "/dev/fd/x", "its", "args"])
чтобы избежать условий гонки (в этом случае $0будет содержать /dev/fd/x).
Теперь, вы можете утверждать , что /dev/fd/x это путь к этому сценарию. Тем не менее, обратите внимание, что если вы читаете из $0, вы нарушите сценарий при использовании ввода.
Теперь есть разница, если имя команды сценария, которое вызывается, не содержит косой черты. В:
the-script its args
Ваша оболочка будет искать the-scriptв $PATH. $PATHможет содержать абсолютные или относительные (включая пустую строку) пути к некоторым каталогам. Например, если $PATHсодержится /bin:/usr/bin:и the-scriptнаходится в текущем каталоге, оболочка выполнит:
exec("the-script", ["the-script", "its", "args"])
который станет:
exec("/bin/sh", ["/bin/sh" or "the-script", "-", "the-script", "its", "args"]
Или если он найден в /usr/bin:
exec("/usr/bin/the-script", ["the-script", "its", "args"])
exec("/bin/sh", ["/bin/sh" or "the-script" or "/usr/bin/the-script",
"-", "/usr/bin/the-script", "its", "args")
Во всех перечисленных выше случаях, за исключением углового случая setuid, $0будет содержаться путь (абсолютный или относительный) к сценарию.
Теперь скрипт также можно назвать так:
the-interpreter the-script its args
Если, the-scriptкак указано выше, символы косой черты не содержат, поведение немного меняется от оболочки к оболочке.
Старые реализации AT & T kshфактически безоговорочно искали сценарий $PATH(который на самом деле являлся ошибкой и дырой в безопасности для сценариев setuid), поэтому $0фактически не содержали пути к сценарию, если только $PATHпоиск не обнаружился the-scriptв текущем каталоге.
Более новые AT & T kshбудут пытаться интерпретировать the-scriptв текущем каталоге, если он читается. Если нет, то он будет искать для чтения и выполнения the-script в $PATH.
Для bash, он проверяет, the-scriptнаходится ли в текущем каталоге (и не является ли это неработающей символической ссылкой), а если нет, ищет читаемый (не обязательно исполняемый) файл the-scriptв $PATH.
zshв shэмуляции будет так, bashза исключением того, что, если the-scriptв текущем каталоге есть неработающая символическая ссылка, он не будет искать the-scriptв $PATHи вместо этого будет сообщать об ошибке.
Все остальные борновоподобные снаряды не заглядывают the-scriptвнутрь $PATH.
В любом случае, для всех этих оболочек, если вы обнаружите, что $0они не содержат /и не читаются, то, вероятно, они были найдены $PATH. Тогда, поскольку файлы в $PATH, вероятно, будут исполняемыми, это, вероятно, безопасное приближение, чтобы использовать его, command -v -- "$0"чтобы найти его путь (хотя это не сработало бы, если $0бы также было имя встроенной оболочки или ключевое слово (в большинстве оболочек)).
Так что, если вы действительно хотите покрыть это дело, вы можете написать это:
progname=$0
[ -r "$progname" ] || progname=$(
IFS=:; set -f
for i in ${PATH-$(getconf PATH)}""; do
case $i in
"") p=$progname;;
*/) p=$i$progname;;
*) p=$i/$progname
esac
[ -r "$p" ] && exec printf '%s\n' "$p"
done
exit 1
) && progname=$(readlink -e -- "$progname") ||
progname=unknown
( ""добавлено, чтобы $PATHсохранить конечный пустой элемент с оболочками, которые $IFSдействуют как разделитель вместо разделителя ).
Теперь есть более эзотерические способы вызова скрипта. Можно сделать:
the-shell < the-script
Или:
cat the-script | the-shell
В этом случае $0будет первый аргумент ( argv[0]), полученный интерпретатором (выше the-shell, но это может быть что угодно, хотя обычно это либо базовое имя, либо один путь к этому интерпретатору).
Обнаружение того, что вы находитесь в такой ситуации на основе значения, $0не является надежным. Вы можете посмотреть на вывод, ps -o args= -p "$$"чтобы получить подсказку. В случае с конвейером, нет никакого реального способа вернуться к пути к сценарию.
Можно также сделать:
the-shell -c '. the-script' blah blih
Тогда, за исключением zsh(и некоторых старых реализаций оболочки Bourne), $0будет blah. Опять же, сложно найти путь к сценарию в этих оболочках.
Или:
the-shell -c "$(cat the-script)" blah blih
и т.п.
Чтобы убедиться, что у вас есть права $progname, вы можете найти в ней определенную строку, например:
progname=$0
[ -r "$progname" ] || progname=$(
IFS=:; set -f
for i in ${PATH-$(getconf PATH)}:; do
case $i in
"") p=$progname;;
*/) p=$i$progname;;
*) p=$i/$progname
esac
[ -r "$p" ] && exec printf '%s\n' "$p"
done
exit 1
) && progname=$(readlink -e -- "$progname") ||
progname=unknown
[ -f "$progname" ] && grep -q 7YQLVVD3UIUDTA32LSE8U9UOHH < "$progname" ||
progname=unknown
Но опять же я не думаю, что это стоит усилий.
$0что-то отличалось от сценария, который действительно отвечал на заголовок вопроса. Однако меня также интересуют ситуации, в которых$0находится сам скрипт, но не включается каталог. В частности, я пытаюсь понять комментарий к такому ответу.