realpathがシンボリックリンクを解決しないようにする方法は?

realpathがシンボリックリンクを解決しないようにする方法は?

私はシンボリックリンクを解決せずにファイルの絶対パスを返すコマンドを探しています。一般に、realpathこれはうまく行われました。

$ 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

また、シンボルでリンクされたディレクトリ内のファイルにも機能します。

$ touch foo/file
$ realpath -se bar/file      # good, this does not resolve the symlink
/tmp/test/bar/file

ただし、県取締役の場合はいシンボリックリンクディレクトリ

$ 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

なぜrealpathこれが起こるのですか? (バグですか?)シンボリックリンクを決して解決できない方法がありますかrealpath、それとも別の方法を使用する必要がありますか?

答え1

プロセスの現在の作業ディレクトリ(CWD)は、オペレーティングシステムレベルの以前のプロセスから継承または使用できますchdir(2)。オペレーティングシステム(ここでは「カーネル」を意味します)はもちろん、最終ディレクトリを決定するために常にすべてのシンボリックリンクを確認してください。結果は(ディレクトリへの)シンボリックリンクではなくディレクトリでなければなりません。たとえば、chdir(2)解決する必要があるシンボリックリンクが多すぎると、古いシステムコール()がエラーを返す可能性があります。ELOOPしたがって、オペレーティングシステムの観点からは、プロセスのディレクトリではないCWDディレクトリはありません。オペレーティングシステムはどこにもシンボリックリンクなしで常にそれを実際のパスとしてチェックします。

シェルが完了すると、cd /tmp/test/barCWDパスはオペレーティングシステムによって確認されます/tmp/test/foo。たとえば、Linuxシステムでは、ls -l /proc/$$/cwdカーネルに表示される確認済みのパスへのリンクが表示されます/tmp/test/foo

barシェルがまだプロンプトに表示されるという事実は、シェルが覚えているからです。CD以前に完了したコマンドです。この動作はシェルの種類によって異なります。ここにbashがあるとします。したがって、これは組み込まれていますがpwd(外部コマンドではありません/bin/pwd)、$PWD変数とその使用は$PS1現在のディレクトリに対してユーザーに「嘘」を付けます。

もちろん、シェルで実行または実行されるすべてのプロセスは継承されrealpathます。/bin/pwd実際CWDは/tmp/test/fooバグではなく、realpathこれに関する具体的な情報はありませんbar

Kusalanandaが提案したように、潜在的に厄介なアプローチは、何らかの方法で変数を再利用し、$PWD変数が絶対的でない場合は引数の前に追加することです。realpath

ここに例があります。悪用する方法があるかどうかはわかりません。たとえば、以下の関数は対処しますが、$PWD変数自体はbash 4.4.12(Debian 9)ではうまく機能しませんが、パスに改行がある場合はbash 5.0.3(Debian 10)ではうまく機能します。どこかに改行文字がある場合に便利な-zオプションを追加する必要がありますrealpathが、この簡単な例ではオプションの完全な解析を再実装しません。

myrealpathnofollowsym () {
    for p in "$@"; do
        if ! printf '%s' "$p" | grep -q -- '^/'; then
            realpath -se "$PWD/$p"
        else
            realpath -se "$p"
        fi
    done
}

関連情報