Procurando pelo utilitário testado para obter o caminho absoluto para um destino de link simbólico relativo

5

NB: No título original deste post, usei a palavra standard no sentido cotidiano de "bem estabelecida" (e, portanto, testada pelo tempo, como um contraste com as soluções rápidas que eu poderia rolar eu mesmo). No contexto do Unix-talk, no entanto, a palavra standard tem um significado técnico muito específico (e muito diferente). Essa alternativa, mais correta, interpretação da palavra padrão no título tornou o resto do meu post inconsistente. (Obrigado a Stéphane Chazelas por apontar isso). Portanto, eu revisei o título, substituindo padrão por testado .

Como exemplo do problema ao qual me refiro no título, suponha que eu tenha a estrutura de diretórios mostrada abaixo

/tmp/example
├── a/
│   └── b/
│       └── c/
│           └── d/
│               └── target
└── A/
    └── B/
        ├── C/
        │   └── D/
        │       └── symlink-0 -> ../../symlink-1/b/c/d/target
        │
        └── symlink-1 -> /tmp/example/a

Observe que /tmp/example/A/B/C/D/symlink-0 é um link simbólico cujo destino imediato é um caminho relativo :

$ readlink /tmp/example/A/B/C/D/symlink-0
../../symlink-1/b/c/d/target

Eu quero pegar o caminho absoluto correspondente a este alvo imediato . IOW, quero executar a resolução parcial

/tmp/example/A/B/C/D/symlink-0 -> /tmp/example/A/B/symlink-1/b/c/d/target

Existe um utilitário Unix padrão (ou pelo menos bem estabelecido e testado pelo tempo) para fazer isso?

Observe que readlink -f resolve os caminhos totalmente (para um caminho livre de links simbólicos); para o caso discutido aqui, por exemplo:

$ readlink -f /tmp/example/A/B/C/D/symlink-0
/tmp/example/a/b/c/d/target

Isso significa que readlink -f não é a resposta para a pergunta que estou fazendo aqui.

Eu poderia criar o meu, adicionando verificação de erros adequada, etc., a algo como a seguinte função zsh :

canonicalize () {
    local abspath=$(dirname $1)/$(readlink $1)
    printf -- '%s\n' $abspath:a
}

... mas aprendi (do jeito difícil) a não subestimar a dificuldade de implementar esse tipo de utilitário de maneira robusta, então prefiro usar ferramentas existentes, se possível.

FWIW, o script abaixo gera o exemplo deste post:

mkdir -p /tmp/example/a/b/c/d /tmp/example/A/B/C/D
touch /tmp/example/a/b/c/d/target
ln -s /tmp/example/a /tmp/example/A/B/symlink-1
ln -s ../../symlink-1/b/c/d/target /tmp/example/A/B/C/D/symlink-0
    
por kjo 19.03.2017 / 20:32

1 resposta

2

Não tenho conhecimento de tal utilidade. No entanto, sua abordagem é falha (e eu concordo que é muito difícil acertar).

O modificador :a calcula um caminho absoluto sem nenhum acesso ao sistema de arquivos. Em particular, ele altera a/b/.. para a , independentemente de o a/b ser um link simbólico ou não.

Se você tiver um link simbólico /a/b -> ../foo , isso será /foo somente se /a não for um link simbólico em si. Mesmo que você canonicize o $(dirname $1) , isso ainda não funcionaria para links simbólicos como /a/b -> x/../../foo if x em si é um symlink.

No seu caso:

/tmp/example/A/B/C/D/symlink-0 -> ../../symlink-1/b/c/d/target

resolve /tmp/example/A/B/symlink-1/b/c/d/target apenas porque nem /tmp/example/A/B/C/D nem /tmp/example/A/B/C são links simbólicos.

Em outras palavras, para obter um caminho absoluto para o destino de um link simbólico livre de .. componentes, talvez seja necessário resolver mais de um link simbólico e não é possível fazê-lo sozinho combinando o caminho do arquivo e o alvo do link simbólico.

Se você quiser esse caminho, a maneira mais fácil (e eu diria apenas razoável e / ou útil) é pegar o caminho canônico daquele destino do link simbólico, que é onde todos os componentes do caminho não são .. , . nem links simbólicos, exceto possivelmente para o último. Para isso, você poderia fazer:

zmodload -F zsh/stat b:zstat
canonicalize1() {
  if [ -L "$1" ]; then
    local link
    zstat -A link +link -- "$1" || return
    case $link in
      (/*) ;;
      (*)
        case $1:h in
          (*/) link=$1:h$link;;
          (*) link=$1:h/$link;;
        esac;;
    esac
    printf '%s\n' $link:h:A/$link:t
  else
    printf '%s\n' $1:A
  fi
}

Em seus links simbólicos /tmp/example/A/B/C/D/symlink-0 , isso ainda dá /tmp/example/a/b/c/d/target , já que o alvo do link simbólico não é um link simbólico, mas se não é isso que você quer, então eu pergunto: qual resultado você quer ao fazer:

canonicalize1 /tmp/x/y

Onde /tmp/x/y é um link simbólico para ../foo e /tmp/x um link simbólico para /a/b/c e /a , /a/b , /a/b/c também são eles próprios links simbólicos?

    
por 31.08.2017 / 22:09