bash - script - linux mostrar caminho




Obtendo o diretório de origem de um script Bash de dentro (20)

Como faço para obter o caminho do diretório em que um script Bash está localizado dentro desse script?

Por exemplo, digamos que eu queira usar um script Bash como um lançador para outro aplicativo. Eu quero mudar o diretório de trabalho para aquele em que o script Bash está localizado, para que eu possa operar nos arquivos desse diretório, da seguinte forma:

$ ./application

Aqui está a maneira simples e correta:

actual_path=$(readlink -f "${BASH_SOURCE[0]}")
script_dir=$(dirname "$actual_path")

Explicação:

  • ${BASH_SOURCE[0]} - o caminho completo para o script. O valor disso estará correto mesmo quando o script estiver sendo gerado, por exemplo, source <(echo 'echo $0') imprime o bash , enquanto que substituí-lo por ${BASH_SOURCE[0]} imprimirá o caminho completo do script. (Claro, isso pressupõe que você está bem em depender do Bash.)

  • readlink -f - Resolve recursivamente quaisquer links simbólicos no caminho especificado. Esta é uma extensão GNU e não está disponível em sistemas BSD (por exemplo). Se você está rodando um Mac, você pode usar o Homebrew para instalar o GNU coreutils e suplantá-lo com o greadlink -f .

  • E, claro, dirname obtém o diretório pai do caminho.


Aqui está um one-liner compatível com POSIX:

SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd \"$SCRIPT_PATH\" && pwd"`

# test
echo $SCRIPT_PATH

Eu comparei muitas das respostas dadas e encontrei algumas soluções mais compactas. Estes parecem lidar com todos os casos de borda louca que surgem de sua combinação favorita de:

  • Caminhos absolutos ou caminhos relativos
  • Links suaves de arquivos e diretórios
  • Invocação como script , script bash script , bash -c script , source script ou . script . script
  • Espaços, guias, novas linhas, unicode, etc. em diretórios e / ou nome de arquivo
  • Nomes de arquivo que começam com um hífen

Se você estiver executando a partir do Linux, parece que usar o identificador proc é a melhor solução para localizar a fonte totalmente resolvida do script atualmente em execução (em uma sessão interativa, o link aponta para o respectivo /dev/pts/X ):

resolved="$(readlink /proc/$$/fd/255 && echo X)" && resolved="${resolved%$'\nX'}"

Isso tem um pouco de feiúra, mas a correção é compacta e fácil de entender. Nós não estamos usando primitivos bash apenas, mas estou bem com isso porque readlink simplifica a tarefa consideravelmente. O echo X adiciona um X ao final da variável string para que qualquer espaço em branco no nome do arquivo não seja comido, e a substituição do parâmetro ${VAR%X} no final da linha se livrar do X Como o readlink adiciona uma nova linha própria (que normalmente seria consumida na substituição do comando se não fosse por nossos truques anteriores), temos que nos livrar disso também. Isso é feito com mais facilidade usando o esquema de cotação $'' , que nos permite usar sequências de escape como \n para representar novas linhas (é também assim que você pode facilmente criar diretórios e arquivos com nomes diferentes).

O acima deve cobrir suas necessidades para localizar o script atualmente em execução no Linux, mas se você não tiver o sistema de arquivos proc à sua disposição, ou se você estiver tentando localizar o caminho totalmente resolvido de algum outro arquivo, então talvez você Você encontrará o código abaixo útil. É apenas uma ligeira modificação do one-liner acima. Se você está brincando com nomes de arquivos / diretórios estranhos, verificar a saída com ls e readlink é informativo, pois ls produzirá caminhos "simplificados", substituindo ? para coisas como novas linhas.

absolute_path=$(readlink -e -- "${BASH_SOURCE[0]}" && echo x) && absolute_path=${absolute_path%?x}
dir=$(dirname -- "$absolute_path" && echo x) && dir=${dir%?x}
file=$(basename -- "$absolute_path" && echo x) && file=${file%?x}

ls -l -- "$dir/$file"
printf '$absolute_path: "%s"\n' "$absolute_path"

Eu não acho que isso é tão fácil quanto os outros o fizeram ser. O pwd não funciona, já que o diretório atual não é necessariamente o diretório com o script. $ 0 nem sempre tem a informação. Considere as três maneiras a seguir para invocar um script.

./script

/usr/bin/script

script

Nas primeiras e terceiras maneiras, $ 0 não tem a informação completa do caminho. No segundo e terceiro, pwd não funcionam. A única maneira de obter o dir na 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 seria simplesmente codificar os dados no diretório / usr / share, e referenciá-los pelo caminho completo. Os dados não devem estar no diretório / usr / bin de qualquer maneira, então essa é provavelmente a coisa a fazer.


Eu usaria algo assim:

# retrieve the full pathname of the called script
scriptPath=$(which $0)

# check whether the path is a link or not
if [ -L $scriptPath ]; then

    # it is a link then retrieve the target path and get the directory name
    sourceDir=$(dirname $(readlink -f $scriptPath))

else

    # otherwise just get the directory name of the script path
    sourceDir=$(dirname $scriptPath)

fi

Isso é específico do Linux, mas você pode usar:

SELF=$(readlink /proc/$$/fd/255)

Isso obtém o diretório de trabalho atual no Mac OS X 10.6.6:

DIR=$(cd "$(dirname "$0")"; pwd)

O comando dirname é o mais básico, simplesmente analisando o caminho até o nome do arquivo da variável $ 0 (nome do script):

dirname "$0"

Mas, como apontou Matt , 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 em qual diretório 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 comando readlink , mas na sua forma mais simples, você pode usar:

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

O readlink resolverá o caminho do script para um caminho absoluto da raiz do sistema de arquivos. Assim, todos os caminhos contendo pontos, tils e / ou links simbólicos simples ou duplos serão resolvidos em um caminho completo.

Aqui está um script demonstrando cada um deles, whatdir.sh:

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename $0`"
echo "dirname: `dirname $0`"
echo "dirname/readlink: $(dirname $(readlink -f $0))"

Executando este script no meu diretório home, usando um caminho relativo:

>>>$ ./whatdir.sh 
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat

Novamente, mas usando o caminho completo para o script:

>>>$ /Users/phatblat/whatdir.sh 
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

Agora mudando de diretórios:

>>>$ cd /tmp
>>>$ ~/whatdir.sh 
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

E finalmente usando um link simbólico para executar o script:

>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh 
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat

Resposta curta:

`dirname $0`

ou ( preferably ):

$(dirname "$0")

Tente usar:

real=$(realpath $(dirname $0))

Use o dirname "$0" :

#!/bin/bash
echo "The script you are running has basename `basename "$0"`, dirname `dirname "$0"`"
echo "The present working directory is `pwd`"

Usar o pwd sozinho não funcionará se você não estiver executando o script do diretório em que ele está contido.

[[email protected] ~]$ pwd
/home/matt
[[email protected] ~]$ ./test2.sh
The script you are running has basename test2.sh, dirname .
The present working directory is /home/matt
[[email protected] ~]$ cd /tmp
[[email protected] tmp]$ ~/test2.sh
The script you are running has basename test2.sh, dirname /home/matt
The present working directory is /tmp

Você pode usar $ BASH_SOURCE

#!/bin/bash

scriptdir=`dirname "$BASH_SOURCE"`

Note que você precisa usar #! / Bin / bash e não #! / Bin / sh já que é uma extensão bash


pwd pode ser usado para encontrar o diretório de trabalho atual, e dirname para encontrar o diretório de um arquivo em particular (o comando que foi executado é $0 , então o nome de dirname $0 deve fornecer o diretório do script atual).

No entanto, dirname fornece precisamente a parte do diretório do nome do arquivo, o que provavelmente será relativo ao diretório de trabalho atual. Se o seu script precisar alterar o diretório por algum motivo, a saída do dirname ficará sem sentido.

Eu sugiro o seguinte:

#!/bin/bash

reldir=`dirname $0`
cd $reldir
directory=`pwd`

echo "Directory is $directory"

Dessa forma, você obtém um diretório absoluto, em vez de relativo.

Como o script será executado em uma instância bash separada, não há necessidade de restaurar o diretório de trabalho posteriormente, mas se você quiser alterar novamente seu script por algum motivo, poderá atribuir facilmente o valor de pwd a uma variável antes você muda de diretório, para uso futuro.

Embora apenas

cd `dirname $0`

resolve o cenário específico na questão, acho que ter o caminho absoluto para mais mais útil em geral.


A melhor solução compacta, a meu ver, seria:

"$( cd "$( echo "${BASH_SOURCE[0]%/*}" )"; pwd )"

Não há confiança em outra coisa senão Bash. O uso de dirname, readlinke basenameacabará por levar a problemas de compatibilidade, para que eles sejam melhor evitados, se possível.


Isso funciona no bash-3.2:

path="$( dirname "$( which "$0" )" )"

Aqui está um exemplo de seu uso:

Digamos que você tenha um diretório ~ / bin , que está em seu $ PATH . Você tem o script A dentro deste diretório. Ele source s roteiro ~ / bin / lib / B . Você sabe onde o script incluído é relativo ao original (o subdiretório lib ), mas não onde está relacionado ao diretório atual do usuário.

Isso é resolvido pelo seguinte (dentro de A ):

source "$( dirname "$( which "$0" )" )/lib/B"

Não importa onde o usuário está ou como ele chama o script, isso sempre funcionará.


Nenhum desses funcionou para um script bash lançado pelo Finder no OS X - acabei usando:

SCRIPT_LOC="`ps -p $$ | sed /PID/d | sed s:.*/Network/:/Network/: |
sed s:.*/Volumes/:/Volumes/:`"

Não é bonito, mas faz o trabalho.


Hmm, se no caminho basename & dirname simplesmente não vai cortá-lo e andar no caminho é difícil (e se o pai não exportou o PATH!). No entanto, o shell tem que ter um identificador aberto para seu script e no bash o identificador é # 255.

SELF=`readlink /proc/$$/fd/255`

funciona para mim.


#!/bin/sh
PRG="$0"

# need this for relative symlinks
while [ -h "$PRG" ] ; do
   PRG=`readlink "$PRG"`
done

scriptdir=`dirname "$PRG"`

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"

é um one-liner útil que lhe dará o nome completo do diretório do script, não importando de onde ele está 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 de diretório estão OK). Se você também quiser resolver os links para o script em si, precisará de uma solução de várias linhas:

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null && pwd )"

Este último irá funcionar com qualquer combinação de aliases, source , bash -c , links simbólicos, etc.

Cuidado: se você cd em um diretório diferente antes de executar este trecho, o resultado pode estar incorreto! Além disso, atente para $CDPATH .

Para entender como funciona, tente executar este formulário mais detalhado:

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  TARGET="$(readlink "$SOURCE")"
  if [[ $TARGET == /* ]]; then
    echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
    SOURCE="$TARGET"
  else
    DIR="$( dirname "$SOURCE" )"
    echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
    SOURCE="$DIR/$TARGET" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
  fi
done
echo "SOURCE is '$SOURCE'"
RDIR="$( dirname "$SOURCE" )"
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"

E vai imprimir algo como:

SOURCE './scriptdir.sh' is a relative symlink to 'sym2/scriptdir.sh' (relative to '.')
SOURCE is './sym2/scriptdir.sh'
DIR './sym2' resolves to '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
DIR is '/home/ubuntu/dotfiles/fo fo/real/real1/real2'

SCRIPT_DIR=$( cd ${0%/*} && pwd -P )




directory