Nos casos mais comuns, $0
conterá um caminho, absoluto ou relativo ao script, portanto
script_path=$(readlink -e -- "$0")
(supondo que exista um readlink
comando e ele suporte -e
) geralmente é uma maneira suficientemente boa de obter o caminho absoluto canônico para o script.
$0
é designado a partir do argumento que especifica o script como passado ao intérprete.
Por exemplo, em:
the-shell -shell-options the/script its args
$0
fica the/script
.
Quando você executa:
the/script its args
Seu shell fará um:
exec("the/script", ["the/script", "its", "args"])
Se o script contiver um #! /bin/sh -
she-bang, por exemplo, o sistema transformará isso em:
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "the/script", "its", "args"])
(se não contiver um she-bang ou, de maneira mais geral, se o sistema retornar um erro ENOEXEC, será o seu shell que fará a mesma coisa)
Há uma exceção para scripts setuid / setgid em alguns sistemas, em que o sistema abrirá o script em alguns fd
x
e será executado:
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "/dev/fd/x", "its", "args"])
para evitar condições de corrida (caso em que $0
conterá /dev/fd/x
).
Agora, você pode argumentar que esse /dev/fd/x
é o caminho para esse script. Observe, no entanto, que, se você ler $0
, interromperá o script ao consumir a entrada.
Agora, há uma diferença se o nome do comando de script chamado não contiver uma barra. No:
the-script its args
Seu shell irá procurar the-script
no $PATH
. $PATH
pode conter caminhos absolutos ou relativos (incluindo a cadeia vazia) para alguns diretórios. Por exemplo, se $PATH
contém /bin:/usr/bin:
e the-script
é encontrado no diretório atual, o shell fará um:
exec("the-script", ["the-script", "its", "args"])
que se tornará:
exec("/bin/sh", ["/bin/sh" or "the-script", "-", "the-script", "its", "args"]
Ou se for encontrado em /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")
Em todos os casos acima, exceto no caso do canto setuid, $0
haverá um caminho (absoluto ou relativo) para o script.
Agora, um script também pode ser chamado como:
the-interpreter the-script its args
Quando, the-script
como acima, não contém caracteres de barra, o comportamento varia ligeiramente de shell para shell.
As ksh
implementações antigas da AT&T estavam realmente pesquisando o script incondicionalmente $PATH
(o que na verdade era um bug e uma brecha na segurança dos scripts setuid); portanto, $0
na verdade, não havia um caminho para o script, a menos que a $PATH
pesquisa realmente acontecesse the-script
no diretório atual.
AT&T mais recente ksh
tentaria interpretar the-script
no diretório atual, se for legível. Se não seria lookup para um legível e executável the-script
em $PATH
.
Para bash
, verifica se the-script
está no diretório atual (e não é um link simbólico quebrado) e, se não, procura uma legível (não necessariamente executável) the-script
em $PATH
.
zsh
em sh
emulação faria como bash
exceto que se the-script
é um link quebrado no diretório atual, não seria procurar um the-script
no $PATH
e, ao invés, relatar um erro.
Todas as outras conchas semelhantes a Bourne não olham the-script
para dentro $PATH
.
De todas as formas, se você achar que $0
não contém um /
e não é legível, provavelmente já foi pesquisado $PATH
. Então, como os arquivos $PATH
provavelmente são executáveis, é provavelmente uma aproximação segura a ser usada command -v -- "$0"
para encontrar o caminho (embora isso não funcione se $0
também for o nome de um shell incorporado ou de uma palavra-chave (na maioria dos shells)).
Portanto, se você realmente deseja cobrir esse caso, escreva-o:
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
(o ""
anexo a $PATH
é preservar um elemento vazio à direita com conchas cujos $IFS
atos sejam delimitadores em vez de separadores ).
Agora, existem maneiras mais esotéricas de invocar um script. Alguém poderia fazer:
the-shell < the-script
Ou:
cat the-script | the-shell
Nesse caso, $0
será o primeiro argumento ( argv[0]
) que o intérprete recebeu (acima the-shell
, mas isso pode ser qualquer coisa, embora geralmente seja o nome da base ou um caminho para esse intérprete).
Detectar que você está nessa situação com base no valor de $0
não é confiável. Você pode olhar para a saída de ps -o args= -p "$$"
para obter uma pista. No caso de pipe, não existe uma maneira real de voltar ao caminho do script.
Pode-se também fazer:
the-shell -c '. the-script' blah blih
Então, exceto em zsh
(e alguma implementação antiga do shell Bourne), $0
seria blah
. Novamente, é difícil chegar ao caminho do script nessas conchas.
Ou:
the-shell -c "$(cat the-script)" blah blih
etc.
Para garantir que você tenha o direito $progname
, pesquise uma string específica, como:
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
Mas, novamente, acho que não vale a pena o esforço.
$0
há algo diferente do script, que responde ao título da pergunta. No entanto, também estou interessado em situações em que$0
está o próprio script, mas não inclui o diretório. Em particular, estou tentando entender o comentário feito na resposta SO.