Dans les cas les plus courants, $0
contiendra un chemin, absolu ou relatif au script, donc
script_path=$(readlink -e -- "$0")
(en supposant qu'il existe une readlink
commande et qu'elle prend en charge -e
) est généralement un assez bon moyen d'obtenir le chemin absolu canonique du script.
$0
est attribué à partir de l'argument spécifiant le script tel qu'il a été transmis à l'interpréteur.
Par exemple, dans:
the-shell -shell-options the/script its args
$0
obtient the/script
.
Lorsque vous exécutez:
the/script its args
Votre shell fera:
exec("the/script", ["the/script", "its", "args"])
Si le script contient un #! /bin/sh -
she-bang par exemple, le système le transformera en:
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "the/script", "its", "args"])
(si elle ne contient pas de coup sec, ou plus généralement si le système retourne une erreur ENOEXEC, alors c'est votre shell qui fera la même chose)
Il existe une exception pour les scripts setuid / setgid sur certains systèmes, où le système ouvrira le script sur certains fd
x
et s'exécutera à la place:
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "/dev/fd/x", "its", "args"])
pour éviter les conditions de course (auquel cas $0
contiendra /dev/fd/x
).
Maintenant, vous pouvez dire que /dev/fd/x
c'est un chemin vers ce script. Notez cependant que si vous lisez $0
, vous romprez le script lorsque vous consommerez l'entrée.
Maintenant, il y a une différence si le nom de la commande de script invoqué ne contient pas de barre oblique. Dans:
the-script its args
Votre shell va chercher the-script
dans $PATH
. $PATH
peut contenir des chemins absolus ou relatifs (y compris la chaîne vide) vers certains répertoires. Par exemple, si $PATH
contient /bin:/usr/bin:
et the-script
se trouve dans le répertoire courant, le shell fera:
exec("the-script", ["the-script", "its", "args"])
qui deviendra:
exec("/bin/sh", ["/bin/sh" or "the-script", "-", "the-script", "its", "args"]
Ou s'il se trouve dans /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")
Dans tous les cas ci-dessus, à l'exception du cas du coin setuid, $0
contiendra un chemin (absolu ou relatif) vers le script.
Maintenant, un script peut également être appelé comme:
the-interpreter the-script its args
Lorsque, the-script
comme ci-dessus, ne contient pas de barres obliques, le comportement varie légèrement d'un shell à l'autre.
Les anciennes ksh
implémentations AT&T recherchaient en fait le script sans condition $PATH
(ce qui était en fait un bogue et une faille de sécurité pour les scripts setuid), donc $0
ne contenaient en fait pas de chemin vers le script à moins que la $PATH
recherche ne se trouve réellement the-script
dans le répertoire actuel.
Un AT&T plus récent ksh
essaierait d'interpréter the-script
dans le répertoire courant s'il est lisible. Sinon, il rechercherait un fichier lisible et exécutablethe-script
dans $PATH
.
Pour bash
, il vérifie s'il se the-script
trouve dans le répertoire courant (et n'est pas un lien symbolique cassé) et sinon, recherche un fichier lisible (pas nécessairement exécutable) the-script
dans $PATH
.
zsh
en sh
émulation ferait comme , bash
sauf que si the-script
est un lien symbolique cassé dans le répertoire courant, il ne serait pas la recherche d'un the-script
dans $PATH
et serait plutôt une erreur.
Tous les autres Bourne comme des obus ne regardent pas the-script
dans $PATH
.
Pour tous ces shells de toute façon, si vous trouvez qu'il $0
ne contient pas de /
et n'est pas lisible, alors il a probablement été recherché $PATH
. Ensuite, comme les fichiers $PATH
sont susceptibles d'être exécutables, c'est probablement une approximation sûre à utiliser command -v -- "$0"
pour trouver son chemin (bien que cela ne fonctionnerait pas s'il $0
se trouve que c'est aussi le nom d'un shell intégré ou d'un mot clé (dans la plupart des shells)).
Donc, si vous voulez vraiment couvrir ce cas, vous pouvez l'écrire:
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
(le ""
annexé à $PATH
est de conserver un élément vide de fin avec des coques dont $IFS
le délimiteur au lieu de séparateur agit ).
Maintenant, il existe des moyens plus ésotériques d'invoquer un script. On pourrait faire:
the-shell < the-script
Ou:
cat the-script | the-shell
Dans ce cas, $0
sera le premier argument ( argv[0]
) que l' interprète a reçu (ci the-shell
- dessus , mais cela peut être n'importe quoi, mais généralement le nom de base ou un chemin d'accès à cet interprète).
Détecter que vous vous trouvez dans cette situation en fonction de la valeur de $0
n'est pas fiable. Vous pouvez regarder la sortie de ps -o args= -p "$$"
pour obtenir un indice. Dans le cas du tuyau, il n'y a aucun moyen réel de revenir à un chemin d'accès au script.
On pourrait aussi faire:
the-shell -c '. the-script' blah blih
Ensuite, sauf dans zsh
(et une ancienne implémentation du shell Bourne), ce $0
serait blah
. Encore une fois, difficile d'accéder au chemin du script dans ces coquilles.
Ou:
the-shell -c "$(cat the-script)" blah blih
etc.
Pour vous assurer que vous avez le droit $progname
, vous pouvez rechercher une chaîne spécifique comme:
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
Mais encore une fois, je ne pense pas que cela en vaille la peine.
$0
il y a autre chose que le script, qui répond au titre de la question. Cependant, je suis également intéressé par les situations où se$0
trouve le script lui-même, mais n'inclut pas le répertoire. En particulier, j'essaie de comprendre le commentaire fait sur la réponse SO.