Como definir o diretório de trabalho atual como o diretório do script?

569

Estou escrevendo um script bash. Eu preciso que o diretório de trabalho atual seja sempre o diretório em que o script está localizado

O comportamento padrão é que o diretório de trabalho atual no script é o do shell no qual eu o executo, mas não quero esse comportamento.

jameshfisher
fonte
Você já pensou em colocar um script wrapper em algum lugar como / usr / bin em cd no diretório apropriado (codificado) e executar seu script?
Dagg Nabbit
2
Por que você precisa do diretório do script? Provavelmente existe uma maneira melhor de resolver o problema subjacente.
bstpierre
12
Gostaria apenas de salientar que o comportamento que você chama de "obviamente indesejável" é de fato inteiramente necessário - se eu executar myscript path/to/file, espero que o script avalie o caminho / para / arquivo em relação ao MEU diretório atual, não em qualquer diretório em que o script ocorra para ser localizado. Além disso, o que aconteceria com um script executado ssh remotehost bash < ./myscriptconforme mencionado no FAQ do BASH?
Gordon Davisson 28/07
3
cd "${BASH_SOURCE%/*}" || exit
caw

Respostas:

656
#!/bin/bash
cd "$(dirname "$0")"
ndim
fonte
7
dirname retorna '.' ao usar o bash no Windows. Então, a resposta de Paulo é melhor.
Tvaroh 28/05
7
Também retorna '.' no Mac OSX
Ben Clayton
4
Vale a pena notar que as coisas podem quebrar se um link simbólico fizer parte $0. No seu script, você pode esperar, por exemplo, ../../consultar o diretório dois níveis acima da localização do script, mas esse não é necessariamente o caso se houver links simbólicos em jogo.
Brian Gordon
20
Se você chamou o script como ./script, .é o diretório correto e a alteração para .ele também terminará no próprio diretório em que scriptestá localizado, ou seja, no diretório de trabalho atual.
Ndim 27/08/14
6
Se você executar o script a partir do diretório atual bash script.sh, o valor de $0é script.sh. A única maneira de o cdcomando "funcionar" para você é porque você não se importa com comandos com falha. Se você usar set -o errexit(aka:) set -epara garantir que seu script não exagere nos comandos com falha, isso NÃO funcionará porque cd script.shé um erro. A maneira confiável [específica do bash] écd "$(dirname ${BASH_SOURCE[0]})"
Bruno Bronosky
322

O seguinte também funciona:

cd "${0%/*}"

A sintaxe é descrita detalhadamente nesta resposta do StackOverflow.

Paul Schulz
fonte
17
Explicação como ele funciona: stackoverflow.com/questions/6393551/...
kenorb
25
Não se esqueça de colocar aspas se o caminho contiver espaço em branco. ou seja, cd "$ {0% / *}"
H.Rabiee 01/08/13
17
Esta falha se o script é chamado com bash script.sh- $0será apenas o nome do arquivo
Chris Watts
17
Eu diria que esta resposta é muito sucinta. Você quase não aprende nada sobre a sintaxe. Alguma descrição sobre como ler isso seria útil.
fraxture
2
Esse truque não gera um subshell também, então é bom para os loucos por velocidade.
teknopaul
184

Experimente as seguintes one-liners simples:


Para todos os sistemas UNIX / OSX / Linux

dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P)

Bater

dir=$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)

Nota: Um traço duplo (-) é usado nos comandos para indicar o final das opções de comando, para que arquivos contendo traços ou outros caracteres especiais não quebrem o comando.

Nota: No Bash, use a ${BASH_SOURCE[0]}favor de $0, caso contrário, o caminho poderá ser interrompido ao buscá-lo ( source/ .).


Para Linux, Mac e outros * BSD:

cd "$(dirname "$(realpath "$0")")";

Nota: realpathdeve ser instalado na distribuição Linux mais popular por padrão (como o Ubuntu), mas em alguns pode estar faltando, então você precisa instalá-lo.

Nota: Se você estiver usando o Bash, use a ${BASH_SOURCE[0]}favor de $0, caso contrário, o caminho poderá ser interrompido ao buscá-lo ( source/ .).

Caso contrário, você poderia tentar algo assim (ele usará a primeira ferramenta existente):

cd "$(dirname "$(readlink -f "$0" || realpath "$0")")"

Para específico do Linux:

cd "$(dirname "$(readlink -f "$0")")"

Usando o GNU readlink no * BSD / Mac:

cd "$(dirname "$(greadlink -f "$0")")"

Nota: Você precisa ter coreutilsinstalado (por exemplo, 1. Instale o Homebrew , 2. brew install coreutils).


Na festança

No bash, você pode usar as Expansões de parâmetros para conseguir isso, como:

cd "${0%/*}"

mas não funcionará se o script for executado no mesmo diretório.

Como alternativa, você pode definir a seguinte função no bash:

realpath () {
  [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

Esta função leva 1 argumento. Se o argumento já tiver um caminho absoluto, imprima-o como está; caso contrário, imprima a $PWDvariável + argumento do nome do arquivo (sem ./prefixo).

ou aqui está a versão tirada do .bashrcarquivo Debian :

function realpath()
{
    f=$@
    if [ -d "$f" ]; then
        base=""
        dir="$f"
    else
        base="/$(basename "$f")"
        dir=$(dirname "$f")
    fi
    dir=$(cd "$dir" && /bin/pwd)
    echo "$dir$base"
}

Relacionado:

Veja também:

Como posso obter o comportamento do readlink -f do GNU em um Mac?

kenorb
fonte
4
uma resposta muito melhor do que as populares porque resolve os syslinks e é responsável por diferentes sistemas operacionais. Obrigado!
Dennis3
Obrigado por esta resposta. O primeiro funcionou no meu Mac ... mas o que a opção - faz no cpcomando, @kenorb?
TobyG
3
Um traço duplo ( --) é usado nos comandos para indicar o final das opções de comando, para que arquivos contendo traços ou outros caracteres especiais não quebrem o comando. Tente, por exemplo, criar o arquivo via touch "-test"e touch -- -test, em seguida, remova o arquivo via rm "-test"e rm -- -test, e veja a diferença.
kenorb
1
Eu removeria o meu upvote se pudesse: realpath está obsoleto unix.stackexchange.com/questions/136494/…
lsh
1
@lsh Diz que apenas o realpathpacote Debian está obsoleto, não o GNU realpath. Se você acha que não está claro, pode sugerir uma edição.
Kenorb
73
cd "$(dirname "${BASH_SOURCE[0]}")"

É fácil. Funciona.

Dan Moulding
fonte
1
Essa deve ser a resposta aceita. Funciona em OSX e Linux Ubuntu.
David Rissato Cruz 5/01/16
5
Isso funciona muito bem quando o script B é originado de outro script A e você precisa usar caminhos relativos ao script B. Obrigado.
Enrico
5
Isso também funciona no Cygwin para aqueles de nós que têm a infelicidade de precisar tocar no Windows.
Bruno Bronosky
3
Oucd "${BASH_SOURCE%/*}" || exit
caw
Funciona no macOS Mojave
Juan Boero
6

A resposta aceita funciona bem para scripts que não foram vinculados a outro lugar, como em $PATH.

#!/bin/bash
cd "$(dirname "$0")"

No entanto, se o script for executado por meio de um link simbólico,

ln -sv ~/project/script.sh ~/bin/; 
~/bin/script.sh

Isso cdará no ~/bin/diretório e não no ~/project/diretório, o que provavelmente interromperá seu script se o objetivo do cdé incluir dependências relativas a~/project/

A resposta segura do link simbólico está abaixo:

#!/bin/bash
cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"

readlink -f é necessário para resolver o caminho absoluto do arquivo potencialmente vinculado.

As aspas são necessárias para dar suporte a caminhos de arquivos que potencialmente podem conter espaços em branco (prática recomendada, mas não é seguro supor que esse não seja o caso)

James McGuigan
fonte
4

Este script parece funcionar para mim:

#!/bin/bash
mypath=`realpath $0`
cd `dirname $mypath`
pwd

A linha de comando pwd ecoa a localização do script como o diretório de trabalho atual, não importa de onde eu o execute.

Amardeep AC9MF
fonte
3
realpathé improvável que seja instalado em qualquer lugar. O que pode não interessar, dependendo da situação do OP.
bstpierre
Meu sistema não possui, realpathmas possui o readlinkque parece ser semelhante.
Pausado até novo aviso.
2
Qual dist não tem o caminho real instalado por padrão? O Ubuntu possui.
Kenorb
1
cd "`dirname $(readlink -f ${0})`"

fonte
Você pode explicar sua resposta, por favor?
Zulu
1
Embora esse código possa ajudar a resolver o problema, ele não explica por que e / ou como responde à pergunta. Fornecer esse contexto adicional melhoraria significativamente seu valor educacional a longo prazo. Por favor edite sua resposta para adicionar explicação, incluindo o que limitações e premissas se aplicam.
Toby Speight
-5
echo $PWD

PWD é uma variável de ambiente.

Maverick Holdem
fonte
5
Isso fornece apenas o diretório chamado de, não o diretório em que o script está.
dagelf
-6

Se você apenas precisar imprimir o diretório de trabalho atual, poderá seguir isso.

$ vim test

#!/bin/bash
pwd
:wq to save the test file.

Dê permissão de execução:

chmod u+x test

Em seguida, execute o script ./teste poderá ver o diretório de trabalho atual.

Chandan
fonte
2
A questão era como garantir que o script seja executado em seu próprio diretório, inclusive se esse não for o diretório de trabalho. Portanto, isso não é uma resposta. Enfim, por que fazer isso em vez de apenas ... correr pwd? Parece um monte de pressionamentos de tecla desperdiçados para mim.
underscore_d