У найпоширеніших випадках $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 -
she-bang, система перетворить це на:
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "the/script", "its", "args"])
(якщо він не містить she-bang, або загалом, якщо система повертає помилку ENOEXEC, то ваша оболонка буде робити те саме)
Існує виняток для встановлених / 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")
У всіх цих випадках, окрім встановленого кутового регістру, $0
буде містити шлях (абсолютний або відносний) до сценарію.
Тепер сценарій також можна назвати так:
the-interpreter the-script its args
Якщо, the-script
як зазначено вище, символи косого рису не містять, поведінка трохи змінюється від оболонки до оболонки.
Старі реалізації AT&T ksh
насправді шукали скрипт беззастережно в $PATH
(який насправді був помилкою та захищеним отвором для встановлених сценаріїв), тому $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
і замість цього повідомляє про помилку.
Всі інші Bourne-подібні снаряди не дивляться 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
(The ""
додаються до $PATH
є збереження косого порожнього елемента з оболонками, $IFS
діє як роздільник замість роздільник ).
Тепер є більше езотеричних способів викликати сценарій. Можна зробити:
the-shell < the-script
Або:
cat the-script | the-shell
У такому випадку $0
буде першим аргументом ( argv[0]
), який отримав інтерпретатор (вище the-shell
, але це може бути що-небудь, хоча загалом або базове ім'я, або один шлях до цього перекладача).
Виявлення того, що ви знаходитесь у цій ситуації на основі значення $0
, не є надійним. Ви можете подивитися на висновок, ps -o args= -p "$$"
щоб отримати підказку. У випадку "pipe" немає реального способу повернутися до шляху до сценарію.
Можна також зробити:
the-shell -c '. the-script' blah blih
Тоді, крім zsh
(і деякої старої реалізації оболонки Борна), $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
є сам сценарій, але він не включає каталог. Зокрема, я намагаюся зрозуміти коментар, зроблений у відповіді на ЗО.