
Estou procurando um comando para retornar o caminho absoluto de um arquivo, sem resolver links simbólicos. Em geral, realpath
faz isso bem.
$ mkdir /tmp/test; cd /tmp/test
$ mkdir foo
$ ln -s foo bar
$ realpath -se bar # good, this does not resolve the symlink
/tmp/test/bar
Também funciona para arquivos dentro de diretórios de links simbólicos.
$ touch foo/file
$ realpath -se bar/file # good, this does not resolve the symlink
/tmp/test/bar/file
No entanto, falha quando o atual diretoréum diretório de link simbólico
$ cd bar
$ pwd
/tmp/test/bar
$ realpath -se file # this fails, returning the target
/tmp/test/foo/file
$ realpath -se . # this also fails, returning the target
/tmp/test/foo
$ realpath -se /tmp/test/bar/file # yet this works
/tmp/test/bar/file
$ realpath -se /tmp/test/bar # and this works
/tmp/test/bar
Por que se realpath
comportaria assim? (É um bug?) Existe uma maneira de realpath
nunca resolver o link simbólico ou existe outro método que devo usar?
Responder1
O diretório de trabalho atual (CWD) de um processo é herdado no nível do sistema operacional do processo anterior ou pode ser alterado para o processo atual usando chdir(2)
. O sistema operacional (aqui quero dizer "o kernel"), é claro, sempre resolverá qualquer link simbólico para determinar o resultado final, que deve ser um diretório, não um link simbólico (para um diretório). Por exemplo, a chamada de sistema anterior ( chdir(2)
) pode retornar o erro ELOOP
quando havia muitos links simbólicos para resolver. Portanto, do ponto de vista do sistema operacional, não pode haver um CWD que não seja um diretório para nenhum processo: o sistema operacional sempre o resolverá no caminho real, sem qualquer link simbólico em qualquer lugar.
Depois que o shell for concluído cd /tmp/test/bar
, o caminho do CWD será resolvido pelo sistema operacional em /tmp/test/foo
. Por exemplo, em um sistema Linux, ls -l /proc/$$/cwd
mostrará o link para o caminho resolvido visto pelo kernel: /tmp/test/foo
.
O fato de o shell ainda exibir bar
seu prompt é porque ele se lembra docdcomando feito antes. O comportamento pode depender do tipo de shell. Vou assumir o bash aqui. Portanto, seu comando interno pwd
(mas não o comando externo /bin/pwd
), a $PWD
variável e seu uso $PS1
"mentirão" para o usuário sobre o diretório atual.
Qualquer processo, como realpath
, ou /bin/pwd
executado a partir do shell, obviamente herdará orealCWD, que é /tmp/test/foo
. Portanto, isso não é um bug realpath
, nunca terá informações específicas sobre o bar
.
Uma possível maneira estranha, conforme sugerido por Kusalananda, é reutilizar de alguma forma a $PWD
variável e anexá-la ao realpath
argumento de apenas se o argumento ainda não for absoluto.
Aqui está um exemplo. Não tenho certeza se não há maneiras de abusar disso. Por exemplo, embora a função abaixo funcione, a $PWD
variável em si não se comporta bem no bash 4.4.12 (Debian 9), mas funciona bem no bash 5.0.3 (Debian 10) se houver um caractere de avanço de linha no caminho. Quando há um avanço de linha em algum lugar, para ser útil, uma -z
opção também deve ser adicionada, realpath
mas não vou reimplementar toda a análise de opções neste exemplo simples.
myrealpathnofollowsym () {
for p in "$@"; do
if ! printf '%s' "$p" | grep -q -- '^/'; then
realpath -se "$PWD/$p"
else
realpath -se "$p"
fi
done
}