W najczęstszych przypadkach $0będzie zawierać ścieżkę bezwzględną lub względną do skryptu, więc
script_path=$(readlink -e -- "$0")
(zakładając, że istnieje readlinkpolecenie 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
$0dostaje 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 xi 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 $0bę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-scriptw $PATH. $PATHmoże zawierać ścieżki bezwzględne lub względne (w tym pusty ciąg) do niektórych katalogów. Na przykład, jeśli $PATHzawiera /bin:/usr/bin:i the-scriptzostanie 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, $0bę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-scriptjak wyżej nie zawiera znaków ukośnika, zachowanie różni się nieznacznie w zależności od powłoki.
Stare kshimplementacje 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 $0naprawdę nie zawierało ścieżki do skryptu, chyba że $PATHwyszukiwanie rzeczywiście zdarzyło się znaleźć the-scriptw bieżącym katalogu.
Nowsze AT&T kshspróbuje zinterpretować the-scriptw 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-scriptznajduje się w bieżącym katalogu (i nie jest zepsutym dowiązaniem symbolicznym), a jeśli nie, poszukaj możliwego do odczytu (niekoniecznie wykonywalnego) the-scriptw $PATH.
zshw shemulacji zrobiłoby to tak, bashże jeśli the-scriptjest zepsute dowiązanie symboliczne w bieżącym katalogu, nie szukałoby the-scriptin $PATHi zamiast tego zgłosiłoby błąd.
Wszystkie inne muszle podobne do Bourne'a nie patrzą w the-scriptgórę $PATH.
W każdym razie, jeśli okaże się, że $0nie zawiera /i nie jest czytelny, prawdopodobnie został sprawdzony $PATH. W związku z tym, ponieważ pliki w plikach $PATHprawdopodobnie są wykonywalne, prawdopodobnie jest to bezpieczne przybliżenie do command -v -- "$0"znalezienia ich ścieżki (chociaż nie zadziałałoby, jeśli $0tak 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 $PATHzachowania końcowego pustego elementu za pomocą powłok, które $IFSdział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 $0bę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, $0nie 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), $0był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.
$0jest coś innego niż skrypt, który odpowiada na tytuł pytania. Jednak interesują mnie również sytuacje, w których$0sam skrypt jest, ale nie zawiera katalogu. W szczególności staram się zrozumieć komentarz podany w odpowiedzi na SO.