Como obtenho o caminho do diretório em que um script Bash está localizado, dentro desse script?
Quero usar um script Bash como iniciador para outro aplicativo. Desejo alterar o diretório de trabalho para o local em que o script Bash está localizado, para que eu possa operar nos arquivos desse diretório, da seguinte maneira:
$ ./application
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd && echo x)"
- e removê-lo sem uma substituição de comando -DIR="${DIR%x}"
.mkdir $'\n'
.dirname
e que o diretório poderia começar com um-
(por exemplo--help
).DIR=$(reldir=$(dirname -- "$0"; echo x); reldir=${reldir%?x}; cd -- "$reldir" && pwd && echo x); DIR=${DIR%?x}
. Talvez isso seja um exagero?Respostas:
é um one-liner útil que fornecerá o nome completo do diretório do script, não importa de onde ele esteja sendo chamado.
Ele funcionará desde que o último componente do caminho usado para encontrar o script não seja um link simbólico (os links do diretório estão OK). Se você também quiser resolver quaisquer links para o próprio script, precisará de uma solução de várias linhas:
Este último irá trabalhar com qualquer combinação de aliases,
source
,bash -c
, links simbólicos, etc.Cuidado: se você estiver
cd
em um diretório diferente antes de executar este trecho, o resultado pode estar incorreto!Além disso, preste atenção às
$CDPATH
dicas e aos efeitos colaterais do stderr se o usuário substituiu o cd de maneira inteligente para redirecionar a saída para o stderr (incluindo sequências de escape, como ao chamar oupdate_terminal_cwd >&2
Mac). Adicionar>/dev/null 2>&1
no final do seucd
comando cuidará das duas possibilidades.Para entender como funciona, tente executar este formulário mais detalhado:
E imprimirá algo como:
fonte
source <script>
ebash <script>
:DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
.cd
imprime algo para STDOUT! Por exemplo, se você$CDPATH
tiver.
. Para cobrir esse caso, useDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )"
dirname $(readlink -f $0)
é o comando certo. Veja gist.github.com/tvlooy/cbfbdb111a4ebad8b93e para uma testcasedirname "$(readlink -f "$0")"
não adiciona complexidade e é uma medida justa mais robusta para a quantidade mínima de problemas.readlink -f $0
dáreadlink: illegal option -- f
.Use
dirname "$0"
:usar
pwd
sozinho não funcionará se você não estiver executando o script a partir do diretório em que está contido.fonte
type -p
se o script for executável. Isso também pode abrir um buraco sutil se o script for executado usandobash test2.sh
e houver outro script com o mesmo nome executável em outro lugar.bash
e a linha hash-bang menciona explicitamente,/bin/bash
eu diria que é bem seguro depender de basismos.dirname $0
é que, se o diretório for o atual, você receberá.
. Tudo bem, a menos que você altere os diretórios no script e espere usar o caminho que obtevedirname $0
como se fosse absoluto. Para obter o caminho absoluto:pushd `dirname $0` > /dev/null
,SCRIPTPATH=`pwd`
,popd > /dev/null
: pastie.org/1489386 (Mas certamente há uma maneira melhor para expandir esse caminho?)dirname $0
é um problema se você atribuí-lo a uma variável e depois usá-lo para iniciar um script como$dir/script.sh
; Eu imagino que este é o caso de uso para esse tipo de coisa 90% do tempo../script.sh
funcionaria bem.O
dirname
comando é o mais básico, basta analisar o caminho até o nome do arquivo da$0
variável (nome do script):Mas, como matt b apontou, o caminho retornado é diferente dependendo de como o script é chamado.
pwd
não faz o trabalho porque isso apenas informa qual é o diretório atual, não o diretório em que o script reside. Além disso, se um link simbólico para um script for executado, você obterá um caminho (provavelmente relativo) para onde o link reside, não o script real.Alguns outros mencionaram o
readlink
comando, mas, na sua forma mais simples, você pode usar:readlink
resolverá o caminho do script para um caminho absoluto a partir da raiz do sistema de arquivos. Portanto, quaisquer caminhos que contenham pontos simples ou duplos, tildes e / ou links simbólicos serão resolvidos para um caminho completo.Aqui está um script demonstrando cada um destes
whatdir.sh
:Executando este script no meu diretório home, usando um caminho relativo:
Novamente, mas usando o caminho completo para o script:
Agora alterando diretórios:
E, finalmente, usando um link simbólico para executar o script:
fonte
readlink
não estará disponível em algumas plataformas na instalação padrão. Tente evitar usá-lo se você puderexport SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
-f
não é reconhecido como uma opção parareadlink
. Usando, emstat -f
vez disso, faz o trabalho. Graçasgreadlink
basicamente oreadlink
que todos conhecemos. Aqui está uma versão independente da plataforma:dir=`greadlink -f ${BASH_SOURCE[0]} || readlink -f ${BASH_SOURCE[0]}`
greadlink
pode ser facilmente instalado através do homebrew:brew install coreutils
Funciona para todas as versões, incluindo
source
" aka.
operador (ponto).$0
é modificado a partir do chamador."./script"
"/full/path/to/script"
"/some/path/../../another/path/script"
"./some/folder/script"
Como alternativa, se o próprio script bash for um link simbólico relativo, você deseja segui-lo e retornar o caminho completo do script vinculado ao:
SCRIPT_PATH
é dado no caminho completo, não importa como é chamado.Apenas certifique-se de localizá-lo no início do script.
Este comentário e código Copyleft, licença selecionável sob a GPL2.0 ou posterior ou CC-SA 3.0 (CreativeCommons Share Alike) ou posterior. (c) 2008. Todos os direitos reservados. Nenhuma garantia de qualquer tipo. Você foi avisado.
http://www.gnu.org/licenses/gpl-2.0.txt
http://creativecommons.org/licenses/by-sa/3.0/
18eedfe1c99df68dc94d4a94712a71aaa8e1e9e36cacf421b9463dd2bbaa02906d0d6656
fonte
readlink -f $(dirname "${VIRTUAL_ENV}")
;dirname "${SCRIPT_PATH}"
&& pwd)? Mas de qualquer maneira ótimo roteiro!cd
sair do diretório atual na esperança decd
retornar novamente mais tarde: O script pode não ter permissão para alterar o diretório novamente para o diretório que estava atual quando foi invocado. (O mesmo vale para pushd / popd)readlink -f
é específico do GNU. O BSDreadlink
não tem essa opção.([ ... ])
é menos eficiente do que[ ... ]
e não há vantagem no isolamento oferecido em troca do desempenho atingido aqui.Resposta curta:
ou ( preferencialmente ):
fonte
source
isso ecd $(dirname $0)
é fácil de lembrar.${BASH_SOURCE[0]}
vez de$0
trabalhar com #source my/script.sh
${BASH_SOURCE[0]}
não é satisfatório.${BASH_SOURCE:-0}
é muito melhor.Você pode usar
$BASH_SOURCE
:Observe que você precisa usar,
#!/bin/bash
e não#!/bin/sh
porque é uma extensão do Bash.fonte
./foo/script
, então$(dirname $BASH_SOURCE)
é./foo
.realpath
comando para obter o caminho completo de ./foo/script. Então,dirname $(realpath ./foo/script)
vai dar o caminho do script.Isso deve servir:
Isso funciona com links simbólicos e espaços no caminho.
Veja as páginas de manual para
dirname
ereadlink
.Na faixa de comentários, parece não funcionar com o Mac OS. Não faço ideia do porquê disso. Alguma sugestão?
fonte
./script.sh
mostra.
em vez do caminho do diretório completostat
vez disso. Mas ainda assim, mostra.
se você está no diretório 'this'.coreutils
partir do Homebrew e usargreadlink
para obter a-f
opção no MacOS, pois é * BSD oculto e não Linux.DIR="$(dirname "$(readlink -f "$0")")"
pwd
pode ser usado para encontrar o diretório de trabalho atual edirname
o diretório de um arquivo específico (o comando que foi executado é$0
, portanto,dirname $0
deve fornecer o diretório do script atual).No entanto,
dirname
fornece com precisão a parte do diretório do nome do arquivo, que provavelmente será relativa ao diretório de trabalho atual. Se o seu script precisar alterar o diretório por algum motivo, a saída dedirname
ficará sem sentido.Sugiro o seguinte:
Dessa forma, você obtém um diretório absoluto, e não relativo.
Como o script será executado em uma instância do bash separada, não há necessidade de restaurar o diretório de trabalho posteriormente, mas se você quiser voltar ao script por algum motivo, poderá atribuir facilmente o valor de
pwd
a uma variável antes de alterar diretório, para uso futuro.Embora apenas
resolve o cenário específico da pergunta, acho que ter o caminho absoluto para mais útil em geral.
fonte
dirname $0
&& pwd)Estou cansado de vir a esta página repetidamente para copiar e colar o verso na resposta aceita. O problema é que não é fácil entender e lembrar.
Aqui está um script fácil de lembrar:
fonte
DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
realpath
resolução "manualmente" com um loop dereadlink
? Até areadlink
página do manual dizNote realpath(1) is the preferred command to use for canonicalization functionality.
realpath
antesdirname
, não depois? Se o arquivo de script em si for um link simbólico ... Daria algo parecidoDIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
. Na verdade, muito perto da resposta proposta por Simon.Eu não acho que isso seja tão fácil quanto os outros fizeram parecer.
pwd
não funciona, pois o diretório atual não é necessariamente o diretório com o script.$0
nem sempre tem as informações. Considere as três maneiras a seguir para invocar um script:Na primeira e na terceira maneira
$0
, não há informações completas do caminho. No segundo e terceiro,pwd
não funciona. A única maneira de obter o diretório da terceira maneira seria percorrer o caminho e encontrar o arquivo com a correspondência correta. Basicamente, o código teria que refazer o que o sistema operacional faz.Uma maneira de fazer o que você está pedindo é apenas codificar os dados no
/usr/share
diretório e referenciá-los por seu caminho completo. Os dados não devem estar no/usr/bin
diretório de qualquer maneira, portanto é provavelmente o que deve ser feito.fonte
fonte
$0
nempwd
é garantido que você tem as informações corretas, dependendo de como o script é chamado.fonte
$BASH_SOURCE
mais$0
, porque é explícito mesmo para os leitores que não conhecem bem o bash.$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
Isso obtém o diretório de trabalho atual no Mac OS X 10.6.6:
fonte
Isso é específico do Linux, mas você pode usar:
fonte
/proc/fd/$$/255
parece apontar para o tty, não para um diretório. Por exemplo, no meu shell de logon atual, os descritores de arquivo 0, 1, 2 e 255 se referem a todos/dev/pts/4
. Em qualquer caso, o manual do bash não menciona fd 255, por isso é provavelmente imprudente depender este comportamento \.realpath ${BASH_SOURCE[0]};
, parece ser o melhor caminho a percorrer.Aqui está uma linha única compatível com POSIX:
fonte
cd
está configurado para imprimir o novo nome do caminho.Eu tentei tudo isso e nenhum funcionou. Um estava muito próximo, mas tinha um pequeno inseto que o quebrou muito; eles esqueceram de colocar o caminho entre aspas.
Muitas pessoas também assumem que você está executando o script a partir de um shell, para que se esqueçam que quando você abre um novo script, o padrão é o seu lar.
Tente este diretório para obter o tamanho:
Isso acerta, independentemente de como ou onde você o executa:
Portanto, para torná-lo realmente útil, veja como mudar para o diretório do script em execução:
fonte
ln -s ../bin64/foo /usr/bin/foo
).Aqui está a maneira simples e correta:
Explicação:
${BASH_SOURCE[0]}
- o caminho completo para o script. O valor disso estará correto mesmo quando o script estiver sendo originado, por exemplo,source <(echo 'echo $0')
imprime bash , ao substituí-lo por${BASH_SOURCE[0]}
imprime o caminho completo do script. (Obviamente, isso pressupõe que você não tenha problemas com o Bash.)readlink -f
- Resolve recursivamente todos os links simbólicos no caminho especificado. Esta é uma extensão GNU e não está disponível em (por exemplo) sistemas BSD. Se você estiver executando um Mac, poderá usar o Homebrew para instalar o GNUcoreutils
e substituí-logreadlink -f
.E, claro,
dirname
obtém o diretório pai do caminho.fonte
greadlink -f
infelizmente, não funciona de forma eficaz quandosource
ing o script no Mac :(A maneira mais curta e elegante de fazer isso é:
Isso funcionaria em todas as plataformas e é super limpo.
Mais detalhes podem ser encontrados em "Em qual diretório está esse script bash? ".
fonte
Eu usaria algo como isto:
fonte
sh
também! Problema comdirname "$0"
soluções simples : se o script estiver no$PATH
e for chamado sem caminho, eles fornecerão resultados incorretos.PATH
,$0
conterá o nome do arquivo absoluto. Se o script for chamado com um nome de arquivo relativo ou absoluto contendo a/
,$0
ele conterá isso.Esta é uma pequena revisão da solução e-satis e 3bcdnlklvc04a, apontada em sua resposta :
Isso ainda deve funcionar em todos os casos listados.
Isso evitará
popd
após uma falhapushd
, graças ao konsolebox.fonte
SCRIPT_DIR=''; pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && { SCRIPT_DIR=$PWD; popd > /dev/null; }
popd
casos (mesmo quando raros) em quepushd
falha. E, casopushd
falhe, qual você acha que deve ser o valorSCRIPT_DIR
? A ação pode variar de acordo com o que pode parecer lógico ou o que um usuário pode preferir, mas certamente, fazer algopopd
errado.pushd
popd
perigos poderiam ser evitados simplesmente descartando-os e usandocd
+pwd
incluído em uma substituição de comando.SCRIPT_DIR=$(...)
Para sistemas com coreutils GNU
readlink
(por exemplo, linux):Não há necessidade de usar
BASH_SOURCE
quando$0
contém o nome do arquivo do script.fonte
dirname
chamada. Necessário se o caminho do diretório contiver espaços.fonte
$0
não funcionará para um script de origem$_
vale a pena mencionar como uma alternativa para$0
. Se você estiver executando um script do Bash, a resposta aceita poderá ser reduzida para:Observe que essa deve ser a primeira declaração do seu script.
fonte
source
ou.
o script. Nessas situações,$_
conteria o último parâmetro do último comando que você executou antes do.
.$BASH_SOURCE
funciona sempre.Comparei muitas das respostas dadas e criei algumas soluções mais compactas. Eles parecem lidar com todos os casos extremos que surgem da sua combinação favorita de:
script
,bash script
,bash -c script
,source script
, ou. script
Se você estiver executando no Linux, parece que usar o
proc
identificador é a melhor solução para localizar a fonte totalmente resolvida do script em execução no momento (em uma sessão interativa, o link aponta para o respectivo/dev/pts/X
):Isso tem um pouco de feiura, mas a correção é compacta e fácil de entender. Não estamos usando apenas os primitivos do bash, mas estou bem com isso, porque
readlink
simplifica a tarefa consideravelmente. Eleecho X
adiciona umX
ao final da cadeia de caracteres variável, para que qualquer espaço em branco no nome do arquivo não seja consumido e a substituição de parâmetro${VAR%X}
no final da linha se livre doX
. Comoreadlink
adiciona uma nova linha própria (que normalmente seria consumida na substituição de comando, se não fosse nossa artimanha anterior), também precisamos nos livrar disso. Isso é feito com mais facilidade usando o para representar novas linhas (também é assim que você pode criar facilmente diretórios e arquivos desonestos).$''
esquema de citações, que nos permite usar seqüências de escape, como\n
O item acima deve cobrir suas necessidades de localização do script atualmente em execução no Linux, mas se você não tiver o
proc
sistema de arquivos à sua disposição ou se estiver tentando localizar o caminho totalmente resolvido de outro arquivo, talvez você encontre o código abaixo útil. É apenas uma pequena modificação do one-liner acima. Se você estiver brincando com nomes de diretório / arquivos estranhos, verifique a saída com ambosls
ereadlink
é informativo, poisls
produzirá caminhos "simplificados", substituindo?
coisas como novas linhas.fonte
/dev/pts/30
bash no Ubuntu 14.10 Desktop.echo $resolved
, eu salva-lo comod
,chmod +x d
,./d
.#!/bin/bash
Tente usar:
fonte
$0
para${BASH_SOURCE[0]}
que esse método funcione em qualquer lugar, inclusive em uma função.dirname
porque a última parte de$0
pode ser um link simbólico que aponta para um arquivo que não está no mesmo diretório que o próprio link simbólico. A solução descrita nesta resposta apenas obtém o caminho do diretório em que o link simbólico armazenava, não o diretório do destino. Além disso, esta solução está faltando citando. Não funcionará se o caminho contiver caracteres especiais.dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
Experimente a seguinte solução compatível com outros:
pois os comandos como
realpath
oureadlink
podem não estar disponíveis (dependendo do sistema operacional).Nota: No Bash, é recomendável usar em
${BASH_SOURCE[0]}
vez de$0
, caso contrário, o caminho pode ser interrompido ao obter o arquivo (source
/.
).Como alternativa, você pode tentar a seguinte função no bash:
Esta função leva 1 argumento. Se o argumento já tiver um caminho absoluto, imprima-o como está; caso contrário, imprima a
$PWD
variável + argumento do nome do arquivo (sem./
prefixo).Relacionado:
fonte
realpath
função @Chris usa 1 argumento. Se o argumento já tiver um caminho absoluto, imprima como está, caso contrário, imprima$PWD
+ nome do arquivo (sem./
prefixo).Eu acredito que eu tenho esse. Estou atrasado para a festa, mas acho que alguns apreciarão estar aqui se eles encontrarem esse tópico. Os comentários devem explicar:
fonte
Estas são maneiras curtas de obter informações de script:
Pastas e arquivos:
Usando estes comandos:
E eu consegui esta saída:
Consulte também: https://pastebin.com/J8KjxrPF
fonte
Isso funciona no bash-3.2:
Se você tem um
~/bin
diretório no seu$PATH
, você temA
dentro deste diretório. Ele origina o script~/bin/lib/B
. Você sabe onde o script incluído é relativo ao original, nolib
subdiretório, mas não onde é relativo ao diretório atual do usuário.Isso é resolvido pelo seguinte (dentro
A
):Não importa onde o usuário está ou como ele chama o script, isso sempre funcionará.
fonte
which
é muito discutível.type
,hash
E outros builtins fazer a mesma coisa melhor em bash.which
é um pouco mais portátil, embora realmente não seja o mesmowhich
usado em outros shells como o tcsh, que o possui como um componente interno.which
Sendo uma ferramenta externa, você não tem motivos para acreditar que ela se comporta de forma idêntica ao shell pai.A melhor solução compacta, na minha opinião, seria:
Não há confiança em nada além de Bash. O uso de
dirname
,readlink
ebasename
acabará por levar a problemas de compatibilidade, para que eles devem ser evitados, se possível.fonte
"$( cd "$( echo "${BASH_SOURCE[0]%/*}/" )"; pwd )"
. Você teria problemas com o diretório raiz, se não o fizer. Além disso, por que você precisa usar o eco?dirname
ebasename
são padronizados para POSIX, então por que evitar usá-los? Links:dirname
,basename