Desejo cumprimentar o script atual para poder imprimir informações de ajuda e versão na seção de comentários na parte superior.
Eu estava pensando em algo assim:
grep '^#h ' -- "$0" | sed -e 's/#h //'
Mas então me perguntei o que aconteceria se o script estivesse localizado em um diretório que estava no PATH e chamado sem especificar explicitamente o diretório.
Procurei uma explicação das variáveis especiais e encontrei as seguintes descrições de $0
:
nome do shell ou programa atual
nome do arquivo do script atual
nome do próprio script
comando como foi executado
Nada disso esclarece se o valor de $0
incluiria ou não o diretório se o script fosse chamado sem ele. O último realmente implica para mim que não.
Teste no meu sistema (Bash 4.1)
Criei um arquivo executável em / usr / local / bin chamado scriptname com uma linha echo $0
e o invoquei de locais diferentes.
Estes são os meus resultados:
> cd /usr/local/bin/test
> ../scriptname
../scriptname
> cd /usr/local/bin
> ./scriptname
./scriptname
> cd /usr/local
> bin/scriptname
bin/scriptname
> cd /tmp
> /usr/local/bin/scriptname
/usr/local/bin/scriptname
> scriptname
/usr/local/bin/scriptname
Nesses testes, o valor de $0
é sempre exatamente como o script foi chamado, exceto se for chamado sem nenhum componente de caminho. Nesse caso, o valor de $0
é o caminho absoluto . Portanto, parece que seria seguro passar para outro comando.
Mas então me deparei com um comentário no Stack Overflow que me confundiu. A resposta sugere usar $(dirname $0)
para obter o diretório do script atual. O comentário (votado 7 vezes) diz "que não funcionará se o script estiver no seu caminho".
Questões
- Esse comentário está correto?
- O comportamento é diferente em outros sistemas?
- Existem situações em
$0
que não incluiria o diretório?
fonte
$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.Respostas:
Nos casos mais comuns,
$0
conterá um caminho, absoluto ou relativo ao script, portanto(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:
$0
ficathe/script
.Quando você executa:
Seu shell fará um:
Se o script contiver um
#! /bin/sh -
she-bang, por exemplo, o sistema transformará isso em:(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: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:
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:
ethe-script
é encontrado no diretório atual, o shell fará um:que se tornará:
Ou se for encontrado em
/usr/bin
: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:
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 acontecessethe-script
no diretório atual.AT&T mais recente
ksh
tentaria interpretarthe-script
no diretório atual, se for legível. Se não seria lookup para um legível e executávelthe-script
em$PATH
.Para
bash
, verifica sethe-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
emsh
emulação faria comobash
exceto que sethe-script
é um link quebrado no diretório atual, não seria procurar umthe-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 usadacommand -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:
(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:
Ou:
Nesse caso,
$0
será o primeiro argumento (argv[0]
) que o intérprete recebeu (acimathe-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 deps -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:
Então, exceto em
zsh
(e alguma implementação antiga do shell Bourne),$0
seriablah
. Novamente, é difícil chegar ao caminho do script nessas conchas.Ou:
etc.
Para garantir que você tenha o direito
$progname
, pesquise uma string específica, como:Mas, novamente, acho que não vale a pena o esforço.
fonte
"-"
nos exemplos acima. Na minha experiência,exec("the-script", ["the-script", "its", "args"])
torna-seexec("/the/interpreter", ["/the/interpreter", "the-script", "its", "args"])
, é claro, a possibilidade de uma opção de intérprete.#! /bin/sh -
é o adágio de boas práticas "sempre usecmd -- something
se você não pode garantir quesomething
não comece com-
" aplicado aqui/bin/sh
(onde-
o marcador de fim de opção é mais portátil que--
) porsomething
ser o caminho / nome do roteiro. Se você não usar isso para scripts setuid (em sistemas que os suportam, mas não com o método / dev / fd / x mencionado na resposta), é possível obter um shell raiz criando um link simbólico para o seu script chamado-i
ou-s
para instância./bin/sh
Desabilite totalmente os scripts setuid ou, de alguma forma , desabilite o próprio processamento de opções, se detectar que está executando o setuid. Não vejo como o uso de / dev / fd / x por si só corrige isso. Você ainda precisa do hífen único / duplo, eu acho./dev/fd/x
começa com/
, não-
. O objetivo principal é remover a condição de corrida entre os doisexecve()
segundos (entre oexecve("the-script")
que eleva os privilégios e o subsequenteexecve("interpreter", "thescript")
ondeinterpreter
abre o script mais tarde (que pode muito bem ter sido substituído por um link simbólico para outra coisa nesse meio tempo)). scripts de suid fazer corretamente umaexecve("interpreter", "/dev/fd/n")
vez que n foi aberto como parte da primeira execve ().Aqui estão duas situações em que o diretório não seria incluído:
Nos dois casos, o diretório atual teria que ser o diretório em que o scriptname estava localizado.
No primeiro caso, o valor de
$0
ainda pode ser passado para,grep
uma vez que assume que o argumento FILE é relativo ao diretório atual.No segundo caso, se as informações de ajuda e versão forem impressas apenas em resposta a uma opção específica da linha de comando, isso não deve ser um problema. Não sei por que alguém chamaria um script dessa maneira para imprimir as informações de ajuda ou versão.
Ressalvas
Se o script alterar o diretório atual, você não desejaria usar caminhos relativos.
Se o script for originado, o valor de
$0
geralmente será o script de chamada em vez do script de origem.fonte
Um argumento arbitrário zero pode ser especificado ao usar a
-c
opção para a maioria dos shells (todos?). Por exemplo:De
man bash
(escolhido puramente porque tem uma descrição melhor que minhaman sh
- o uso é o mesmo, independentemente):fonte
argv0
é seu primeiro argumento de linha de comando não operacional.OBSERVAÇÃO: Outros já explicaram a mecânica, por
$0
isso vou pular tudo isso.Eu passo geralmente lado toda esta questão e simplesmente usar o comando
readlink -f $0
. Isso sempre lhe dará de volta o caminho completo do que você der como argumento.Exemplos
Digamos que estou aqui para começar:
Crie um diretório + arquivo:
Agora comece a exibir
readlink
:Truques adicionais
Agora, com um resultado consistente sendo retornado quando interrogamos
$0
viareadlink
, podemos usar um simplesmentedirname $(readlink -f $0)
para obter o caminho absoluto para o script - ou -basename $(readlink -f $0)
para obter o nome real do script.fonte
Minha
man
página diz:Parece que isso se traduz
argv[0]
no shell atual - ou no primeiro argumento de linha de comando não operacional e no qual o shell atualmente em interpretação é alimentado quando invocado. I declarou anteriormente quesh ./somescript
caminho seria a sua variável de mas esta foi incorreta porque o é um novo processo de sua própria e invocado com um novo .$0 $ENV
sh
shell
$ENV
Dessa maneira,
sh ./somescript.sh
difere do. ./somescript.sh
que é executado no ambiente atual e$0
já está definido.Você pode verificar isso comparando
$0
com/proc/$$/status
.Obrigado pela correção, @toxalot. Eu aprendi alguma coisa
fonte
. ./myscript.sh
ousource ./myscript.sh
),$0
é o shell. Mas se for passado como argumento para o shell (sh ./myscript.sh
), então$0
é o caminho para o script. Claro,sh
no meu sistema é o Bash. Então, eu não sei se isso faz diferença ou não.exec
e, em vez disso, procurá-la, mas com ela, deveriaexec
../somescript
com o bangline#!/bin/sh
, seria equivalente a executar/bin/sh ./somescript
. Caso contrário, eles não fazem diferença para o casco.Embora
$0
contenha o nome do script, ele pode conter um caminho prefixado com base na maneira como o script é chamado, sempre usei${0##*/}
para imprimir o nome do script na saída da ajuda que remove qualquer caminho principal$0
.Retirado do guia Advanced Bash Scripting - Seção 10.2, substituição de parâmetros
Portanto, a parte mais longa
$0
dessas correspondências*/
será o prefixo do caminho inteiro, retornando apenas o nome do script.fonte
Para algo semelhante, eu uso:
rPath
sempre tem o mesmo valor.fonte