W najczęstszych przypadkach $0
będzie zawierać ścieżkę bezwzględną lub względną do skryptu, więc
script_path=$(readlink -e -- "$0")
(zakładając, że istnieje readlink
polecenie i obsługuje -e
) ogólnie jest wystarczającym sposobem na uzyskanie kanonicznej bezwzględnej ścieżki do skryptu.
$0
jest przypisywany z argumentu określającego skrypt przekazany interpreterowi.
Na przykład w:
the-shell -shell-options the/script its args
$0
dostaje the/script
.
Kiedy biegniesz:
the/script its args
Twoja powłoka wykona:
exec("the/script", ["the/script", "its", "args"])
Jeśli skrypt zawiera na przykład #! /bin/sh -
she-bang, system przekształci go w:
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "the/script", "its", "args"])
(jeśli nie zawiera she-bang, lub bardziej ogólnie, jeśli system zwraca błąd ENOEXEC, to jego powłoka zrobi to samo)
Istnieje wyjątek dla skryptów setuid / setgid w niektórych systemach, gdzie system otworzy skrypt na niektórych fd
x
i uruchomi zamiast niego:
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "/dev/fd/x", "its", "args"])
aby uniknąć warunków wyścigu (w takim przypadku $0
będzie to zawierać /dev/fd/x
).
Teraz możesz argumentować, że /dev/fd/x
jest to ścieżka do tego skryptu. Pamiętaj jednak, że jeśli czytasz z $0
, złamiesz skrypt, gdy zużyjesz dane wejściowe.
Istnieje różnica, jeśli wywołana nazwa polecenia skryptu nie zawiera ukośnika. W:
the-script its args
Twoja powłoka będzie patrzeć the-script
w $PATH
. $PATH
może zawierać ścieżki bezwzględne lub względne (w tym pusty ciąg) do niektórych katalogów. Na przykład, jeśli $PATH
zawiera /bin:/usr/bin:
i the-script
zostanie znaleziony w bieżącym katalogu, powłoka wykona:
exec("the-script", ["the-script", "its", "args"])
który stanie się:
exec("/bin/sh", ["/bin/sh" or "the-script", "-", "the-script", "its", "args"]
Lub jeśli zostanie znaleziony w /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")
We wszystkich powyższych przypadkach, z wyjątkiem przypadków narożników setuid, $0
będzie zawierać ścieżkę (bezwzględną lub względną) do skryptu.
Teraz skrypt można również wywołać jako:
the-interpreter the-script its args
Gdy the-script
jak wyżej nie zawiera znaków ukośnika, zachowanie różni się nieznacznie w zależności od powłoki.
Stare ksh
implementacje AT&T faktycznie szukały skryptu bezwarunkowo w $PATH
(co było w rzeczywistości błędem i luką w zabezpieczeniach dla skryptów setuid), więc tak $0
naprawdę nie zawierało ścieżki do skryptu, chyba że $PATH
wyszukiwanie rzeczywiście zdarzyło się znaleźć the-script
w bieżącym katalogu.
Nowsze AT&T ksh
spróbuje zinterpretować the-script
w bieżącym katalogu, jeśli jest to możliwe do odczytania. Jeśli nie, to szukałby czytelnego i wykonywalnego the-script
w $PATH
.
Sprawdza bowiem bash
, czy the-script
znajduje się w bieżącym katalogu (i nie jest zepsutym dowiązaniem symbolicznym), a jeśli nie, poszukaj możliwego do odczytu (niekoniecznie wykonywalnego) the-script
w $PATH
.
zsh
w sh
emulacji zrobiłoby to tak, bash
że jeśli the-script
jest zepsute dowiązanie symboliczne w bieżącym katalogu, nie szukałoby the-script
in $PATH
i zamiast tego zgłosiłoby błąd.
Wszystkie inne muszle podobne do Bourne'a nie patrzą w the-script
górę $PATH
.
W każdym razie, jeśli okaże się, że $0
nie zawiera /
i nie jest czytelny, prawdopodobnie został sprawdzony $PATH
. W związku z tym, ponieważ pliki w plikach $PATH
prawdopodobnie są wykonywalne, prawdopodobnie jest to bezpieczne przybliżenie do command -v -- "$0"
znalezienia ich ścieżki (chociaż nie zadziałałoby, jeśli $0
tak się składa, że jest to nazwa wbudowanej powłoki lub słowa kluczowego (w większości powłok)).
Więc jeśli naprawdę chcesz ukryć sprawę, możesz ją napisać:
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
( ""
dołączono do $PATH
zachowania końcowego pustego elementu za pomocą powłok, które $IFS
działają jak separator zamiast separatora ).
Teraz są bardziej ezoteryczne sposoby na wywołanie skryptu. Można zrobić:
the-shell < the-script
Lub:
cat the-script | the-shell
W takim przypadku $0
będzie to pierwszy argument ( argv[0]
) otrzymany przez interpretera (powyżej the-shell
, ale może to być cokolwiek, ale ogólnie albo basename, albo jedna ścieżka do tego interpretera).
Wykrywanie, że jesteś w takiej sytuacji na podstawie wartości, $0
nie jest wiarygodne. Możesz spojrzeć na wynik, ps -o args= -p "$$"
aby uzyskać wskazówkę. W przypadku potoku nie ma prawdziwego sposobu na powrót do ścieżki do skryptu.
Można również zrobić:
the-shell -c '. the-script' blah blih
Wtedy, z wyjątkiem zsh
(i niektórych starych implementacji powłoki Bourne'a), $0
byłoby blah
. Ponownie trudno jest znaleźć ścieżkę skryptu w tych powłokach.
Lub:
the-shell -c "$(cat the-script)" blah blih
itp.
Aby upewnić się, że masz do tego prawo $progname
, możesz wyszukać w nim określony ciąg znaków, np .:
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
Ale znowu nie sądzę, żeby było warto.
$0
jest coś innego niż skrypt, który odpowiada na tytuł pytania. Jednak interesują mnie również sytuacje, w których$0
sam skrypt jest, ale nie zawiera katalogu. W szczególności staram się zrozumieć komentarz podany w odpowiedzi na SO.