encontrando o diretório absoluto

3

Aqui está o cenário:

foo/
> data/
> stuff/
> scripts/ -> /.../generic/scripts

Ao chamar foo/scripts/bar.sh de qualquer local (outra pasta ou dentro da pasta), eu gostaria que o script usasse foo/data e foo/stuff

No entanto, se eu simplesmente usar foo_dir='dirname $0'/.. , ele irá para o diretório apontado pelo link simbólico! (E obtenha generic em vez de foo !)

Se eu usar algum padrão para consumir o final de $0 , ele também não funcionará, pois você também pode chamar o script de dentro do diretório.

Então, no final, a única coisa que eu encontrei é essa coisa extremamente feia:

dir='dirname $0'
cd $dir
dir='pwd | sed -r 's_/[^/]+$__''
cd -

... mas tenho certeza que existe uma maneira melhor, não é?

O que eu usei:

dir=$( dirname $( cd 'dirname $0'; pwd ) )

Não sei se é perfeito, mas parece se comportar como esperado.

    
por dagnelies 05.06.2011 / 22:48

6 respostas

1

Desculpe por não ler sua pergunta com mais cuidado na primeira vez.

Em vez de:

foo_dir = $(dirname $0)/..

que tal:

foo_dir = $(dirname $(dirname $0))

No entanto, isso só funciona se houver pelo menos um componente de diretório em $ 0. Você tem essa garantia?

    
por 07.06.2011 / 18:09
4

Você pode usar o seguinte one-liner:

DIR="$(cd "$( dirname "$0" )" && pwd )"
    
por 05.06.2011 / 23:33
1

Sim, .. na presença de links simbólicos é um problema real. Rob Pike escreveu um artigo completo sobre esse problema .

Sugiro que você evite .. usando dirname com um nome de caminho absoluto, assim:

case $0 in
  /*) where="$0" ;;
  *)  where="$(pwd)/$0" ;;
esac
# postcondition: $where is an absolute pathname for script
# N.B $where = .../foo/scripts/command

base="$(dirname "$(dirname "$where")")"
# postcondition: $base = .../foo

resource="$base/data"

Eu realmente uso o primeiro idioma (recuperar um nome de caminho absoluto de um possivelmente relativo) que eu tenho um script apenas para isso no meu diretório ~/bin .

Assim que você estiver lidando com nomes de caminhos absolutos, dirname se comportará de maneira sensata.

    
por 07.06.2011 / 00:43
0

Ok, então o que você quer é que o script seja acessado através do symlink para poder usar arquivos relativos ao symlink, certo? E para que isso funcione mesmo que o caminho usado seja absoluto, relativo, relativo de dentro do mesmo diretório, relativo de um subdiretório, etc?

Isto não é de fato simples. Surpreendentemente simples, na verdade - muitas linguagens de programação têm uma função 'abspath' que resolveria esse problema em um flash. No entanto, não acredito que exista tal função no kit de ferramentas de script de shell padrão.

No entanto, você pode criar um simples, com uma linha única:

abspath() {
    { [[ "$1" =~ ^/ ]] && echo "$1" || echo "$(pwd)/$1"; } | sed -r ':. s#(/|^)\./##g; t .; :: s#[^/]{1,}/\.\./##; t :'
}
foo_dir=$(dirname $(abspath $0))

Acredito que isso seja totalmente genérico e funcione para caminhos que contenham qualquer quantidade de ./ e ../ .

Ele usa expressões regulares um pouco,

    
por 06.06.2011 / 19:40
0

Sua postagem é um pouco desconexa e algumas das declarações parecem contraditórias, mas se meu entendimento estiver correto, você tem um $0 da forma foo/scripts/bar.sh e está tentando extrair a foo parte. Além disso, você garante que sempre haverá pelo menos dois níveis de diretórios nesse caminho (se você tiver apenas bar.sh , você está torrado). Então você pode apenas remover os dois últimos componentes do caminho:

DIR=${0%/*/*}

Você poderia chamar seu script através de scripts/bar.sh , no entanto, nesse caso, a substituição do padrão simples não funcionaria. Para não mencionar chamar o script via $PATH . Você pode torná-lo condicional:

case $0 in
  */*/*) DIR=${0%/*/*};;
  ?*/*) DIR=.;;
  /*) echo 1>&2 "Storing $0 in the root directory is not supported, aborting."
      exit 125;;
  *) # The script was called through the 'PATH'
    IFS=:; set -f; unset DIR
    for d in $PATH; do
      if [ -x "$d/$0" ]; then
        # If you put relative directories in your PATH, you get what you deserve.
        DIR=${d%/*};;
      fi
    done
    if [ -z "$DIR" ]; then
      echo 1>&2 "$0: Fatal error: I can't find myself."
      exit 125
    fi
    unset IFS; set +f;;
esac
    
por 07.06.2011 / 00:19
0

Basta ligar para o Perl:

abspath=$(perl -MCwd -MFile::Basename -e 'print abs_path(dirname($ARGV[0]))' $0)

Observe que abs_path(dirname(...)) pode não ser o mesmo que dirname(abs_path(...)) .

(Editar: adicionado -MFile::Basename )

    
por 07.06.2011 / 03:09