Nos casos mais comuns, $0conterá um caminho, absoluto ou relativo ao script, portanto
script_path=$(readlink -e -- "$0")
(supondo que exista um readlinkcomando 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
$0fica 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 xe será executado:
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "/dev/fd/x", "its", "args"])
para evitar condições de corrida (caso em que $0conterá /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-scriptno $PATH. $PATHpode conter caminhos absolutos ou relativos (incluindo a cadeia vazia) para alguns diretórios. Por exemplo, se $PATHconté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, $0haverá 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-scriptcomo acima, não contém caracteres de barra, o comportamento varia ligeiramente de shell para shell.
As kshimplementaçõ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, $0na verdade, não havia um caminho para o script, a menos que a $PATHpesquisa realmente acontecesse the-scriptno diretório atual.
AT&T mais recente kshtentaria interpretar the-scriptno 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-scriptestá 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-scriptem $PATH.
zshem shemulação faria como bashexceto que se the-scripté um link quebrado no diretório atual, não seria procurar um the-scriptno $PATHe, ao invés, relatar um erro.
Todas as outras conchas semelhantes a Bourne não olham the-scriptpara dentro $PATH.
De todas as formas, se você achar que $0não contém um /e não é legível, provavelmente já foi pesquisado $PATH. Então, como os arquivos $PATHprovavelmente 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 $0també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 $IFSatos 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, $0será 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 $0nã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), $0seria 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.
$0há algo diferente do script, que responde ao título da pergunta. No entanto, também estou interessado em situações em que$0está o próprio script, mas não inclui o diretório. Em particular, estou tentando entender o comentário feito na resposta SO.