В наиболее распространенных случаях $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
находится сам скрипт, но не включается каталог. В частности, я пытаюсь понять комментарий к такому ответу.