Nei casi più comuni, $0
conterrà un percorso, assoluto o relativo allo script, quindi
script_path=$(readlink -e -- "$0")
(supponendo che ci sia un readlink
comando e che supporti -e
) generalmente è un modo abbastanza buono per ottenere il percorso assoluto canonico allo script.
$0
viene assegnato dall'argomento specificando lo script come passato all'interprete.
Ad esempio, in:
the-shell -shell-options the/script its args
$0
ottiene the/script
.
Quando corri:
the/script its args
La tua shell farà un:
exec("the/script", ["the/script", "its", "args"])
Se lo script contiene un #! /bin/sh -
botto per esempio, il sistema lo trasformerà in:
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "the/script", "its", "args"])
(se non contiene un botto o, più in generale, se il sistema restituisce un errore ENOEXEC, allora è la tua shell che farà la stessa cosa)
C'è un'eccezione per gli script setuid / setgid su alcuni sistemi, in cui il sistema aprirà lo script su alcuni fd
x
ed eseguirà invece:
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "/dev/fd/x", "its", "args"])
per evitare le condizioni di gara (nel qual caso $0
conterrà /dev/fd/x
).
Ora, potresti obiettare che /dev/fd/x
è un percorso per quello script. Si noti tuttavia che se si legge da $0
, si romperà lo script mentre si consuma l'input.
Ora, c'è una differenza se il nome del comando di script come invocato non contiene una barra. Nel:
the-script its args
La shell cercherà the-script
in $PATH
. $PATH
può contenere percorsi assoluti o relativi (inclusa la stringa vuota) in alcune directory. Ad esempio, se $PATH
contiene /bin:/usr/bin:
e the-script
si trova nella directory corrente, la shell farà un:
exec("the-script", ["the-script", "its", "args"])
che diventerà:
exec("/bin/sh", ["/bin/sh" or "the-script", "-", "the-script", "its", "args"]
O se si trova in /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")
In tutti quei casi sopra, tranne il caso d'angolo setuid, $0
conterrà un percorso (assoluto o relativo) allo script.
Ora, uno script può anche essere chiamato come:
the-interpreter the-script its args
Quando the-script
come sopra non contiene caratteri barra, il comportamento varia leggermente da shell a shell.
Le vecchie ksh
implementazioni AT&T stavano effettivamente cercando lo script incondizionatamente $PATH
(che in realtà era un bug e una falla di sicurezza per gli script setuid), quindi in $0
realtà non conteneva un percorso dello script a meno che la $PATH
ricerca non fosse effettivamente trovata the-script
nella directory corrente.
Gli AT&T più recenti ksh
tenterebbero di interpretare the-script
nella directory corrente se è leggibile. In caso contrario, cercherebbe un file leggibile ed eseguibilethe-script
in $PATH
.
Per bash
, controlla se si the-script
trova nella directory corrente (e non è un link simbolico non funzionante) e, in caso contrario, cerca un leggibile (non necessariamente eseguibile) the-script
in $PATH
.
zsh
in sh
emulazione farebbe bash
eccezione se non the-script
fosse un collegamento simbolico interrotto nella directory corrente, non cercherebbe un the-script
in $PATH
e segnalerebbe invece un errore.
Tutte le altre conchiglie tipo Bourne non guardano the-script
dentro $PATH
.
Per tutte quelle shell comunque, se trovi che $0
non contiene un /
e non è leggibile, probabilmente è stato cercato in $PATH
. Quindi, poiché $PATH
è probabile che i file siano eseguibili, è probabilmente un'approssimazione sicura da usare command -v -- "$0"
per trovare il suo percorso (anche se non funzionerebbe se si $0
verifica anche il nome di una shell incorporata o di una parola chiave (nella maggior parte delle shell)).
Quindi, se vuoi davvero coprire questo caso, potresti scriverlo:
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
(l' ""
aggiunta a $PATH
è di preservare un elemento vuoto finale con shell le cui $IFS
funzioni sono delimitatori anziché separatori ).
Ora, ci sono modi più esoterici per invocare uno script. Si potrebbe fare:
the-shell < the-script
O:
cat the-script | the-shell
In tal caso, $0
sarà il primo argomento ( argv[0]
) che l' interprete ha ricevuto (sopra the-shell
, ma potrebbe essere qualsiasi cosa, sebbene generalmente sia il nome di base o un percorso per quell'interprete).
Rilevare che ti trovi in quella situazione in base al valore di $0
non è affidabile. Potresti guardare l'output di ps -o args= -p "$$"
per ottenere un indizio. Nel caso della pipe, non c'è modo reale di tornare a un percorso dello script.
Si potrebbe anche fare:
the-shell -c '. the-script' blah blih
Quindi, tranne in zsh
(e qualche vecchia implementazione della shell Bourne), $0
sarebbe blah
. Ancora una volta, difficile arrivare al percorso dello script in quelle shell.
O:
the-shell -c "$(cat the-script)" blah blih
eccetera.
Per assicurarti di avere il diritto $progname
, puoi cercare una stringa specifica in esso come:
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
Ma di nuovo non penso che valga la pena.
$0
c'è qualcosa di diverso dalla sceneggiatura, che risponde al titolo della domanda. Tuttavia, sono anche interessato a situazioni in cui si$0
trova lo script stesso, ma non include la directory. In particolare, sto cercando di capire il commento fatto sulla risposta SO.