Como posso fazer com que o realpath nunca resolva links simbólicos?

Como posso fazer com que o realpath nunca resolva links simbólicos?

Estou procurando um comando para retornar o caminho absoluto de um arquivo, sem resolver links simbólicos. Em geral, realpathfaz 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 realpathcomportaria assim? (É um bug?) Existe uma maneira de realpathnunca 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 ELOOPquando 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/$$/cwdmostrará o link para o caminho resolvido visto pelo kernel: /tmp/test/foo.

O fato de o shell ainda exibir barseu 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 $PWDvariável e seu uso $PS1"mentirão" para o usuário sobre o diretório atual.

Qualquer processo, como realpath, ou /bin/pwdexecutado 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 $PWDvariável e anexá-la ao realpathargumento 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 $PWDvariá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 -zopção também deve ser adicionada, realpathmas 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
}

informação relacionada