Nos casos mais comuns, $0
conterá um caminho, absoluto ou relativo ao script, então
script_path=$(readlink -e -- "$0")
(assumindo que há um comando readlink
e ele suporta -e
) geralmente é uma maneira boa de obter o caminho absoluto canônico para o script.
$0
é atribuído a partir do argumento especificando o script como passado para o interpretador.
Por exemplo, em:
the-shell -shell-options the/script its args
$0
obtém 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, mais geralmente, se o sistema retornar um erro ENOEXEC, então seu shell fará a mesma coisa)
Há uma exceção para scripts setuid / setgid em alguns sistemas, em que o sistema abrirá o script em algum 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 /dev/fd/x
é um caminho para esse script. No entanto, observe que, se você ler $0
, você quebrará o script enquanto consome a entrada.
Agora, há uma diferença se o nome do comando de script como invocado não contiver uma barra. Em:
the-script its args
Seu shell consultará the-script
em $PATH
. $PATH
pode conter caminhos absolutos ou relativos (incluindo a cadeia vazia) para alguns diretórios. Por exemplo, se $PATH
contiver /bin:/usr/bin:
e the-script
for encontrado no diretório atual, o shell fará o seguinte:
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
conterá 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 implementações do AT & T ksh
antigo estavam realmente procurando o script incondicionalmente em $PATH
(que na verdade era um bug e uma falha de segurança para scripts setuid), então $0
na verdade não contém um caminho para o script, a menos que a pesquisa $PATH
realmente tenha encontrado the-script
no diretório atual.
O mais novo AT & T ksh
tentaria interpretar the-script
no diretório atual se for legível. Se não, procuraria por 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 danificado) e, caso contrário, procure por um legível (não necessariamente executável) the-script
in $PATH
.
zsh
em sh
emulação seria como bash
, exceto que se the-script
fosse um link simbólico quebrado no diretório atual, ele não procuraria por the-script
em $PATH
e, em vez disso, reportaria um erro.
Todos os outros shells parecidos com Bourne não parecem em the-script
em $PATH
.
Para todos os shells de qualquer maneira, se você achar que $0
não contém /
e não é legível, provavelmente ele foi pesquisado em $PATH
. Então, como os arquivos em $PATH
provavelmente serão executáveis, provavelmente é uma aproximação segura usar command -v -- "$0"
para encontrar seu caminho (embora isso não funcione se $0
também for o nome de um shell embutido ou palavra-chave (na maioria dos shells)).
Então, se você realmente quer cobrir esse caso, você pode escrever:
progname=$0
[ -r "$progname" ] || progname=$(
PATH_terminated=${PATH-$(getconf PATH)}:
IFS=:; set -f
for i in $PATH_terminated; 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
(aqui assumindo um shell POSIX onde o IFS é o delimitador de campo . Shells como zsh
(em sh
emulation) ou antigos pdksh
-based que tratam o IFS como um separador de campo verá um elemento vazio extra na divisão de $PATH_terminated
).
Agora, existem maneiras mais esotéricas de invocar um script. Pode-se 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 de the-shell
, mas pode ser qualquer coisa, geralmente 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ê poderia olhar a saída de ps -o args= -p "$$"
para obter uma pista. No caso de tubos, não há uma maneira real de voltar ao caminho para o script.
Também se pode fazer:
the-shell -c '. the-script' blah blih
Em seguida, exceto em zsh
(e alguma implementação antiga do shell Bourne), $0
seria blah
. Novamente, é difícil chegar ao caminho do script nesses shells.
Ou:
the-shell -c "$(cat the-script)" blah blih
etc.
Para garantir que você tenha o $progname
correto, pesquise uma string específica, como:
progname=$0
[ -r "$progname" ] || progname=$(
PATH_terminated=${PATH-$(getconf PATH)}:
IFS=:; set -f
for i in $PATH_terminated; 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, não acho que valha a pena o esforço.