
シンボリックリンクを解決せずにファイルの絶対パスを返すコマンドを探しています。 一般的に、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) は、OS レベルで前のプロセスから継承されるか、 を使用して現在のプロセス用に変更することができますchdir(2)
。OS (ここでは「カーネル」を意味します) は、もちろん、常にシンボリックリンクを解決して最終結果を決定しますが、最終結果はディレクトリであり、シンボリックリンク (ディレクトリへの) ではありません。たとえば、前のシステム コール ( chdir(2)
) は、解決するシンボリック リンクが多すぎる場合にエラーを返すことができますELOOP
。したがって、OS の観点からは、どのプロセスに対してもディレクトリではない CWD は存在できません。OS は常に、どこにもシンボリックリンクのない実際のパスとしてそれを解決します。
シェルが を実行するとcd /tmp/test/bar
、CWD パスは OS によって に解決されます/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 が提案しているように、1 つの厄介な方法は、何らかの方法で変数を再利用し$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
}