同じディレクトリを指すシンボリックリンクをたどり続けるとどうなるかを確認するために、小さなbashスクリプトを作成しました。非常に長い作業ディレクトリを作成するか、クラッシュすることを期待していました。しかし、その結果は私を驚かせました…
mkdir a
cd a
ln -s ./. a
for i in `seq 1 1000`
do
cd a
pwd
done
出力の一部は
です${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a
${HOME}/a/a
${HOME}/a/a/a
${HOME}/a/a/a/a
${HOME}/a/a/a/a/a
${HOME}/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a
ここで何が起こっているのですか?
承認された回答:
パトリスは彼の答えで問題の原因を特定しましたが、そこからどのようにそれを得るのかを知りたい場合は、ここに長い話があります。
プロセスの現在の作業ディレクトリは、複雑すぎるとは思わないでしょう。これは、相対パス(プロセスによって行われるシステムコール内)が開始するディレクトリタイプのファイルへのハンドルであるプロセスの属性です。相対パスを解決する場合、カーネルは(a)現在のディレクトリへのフルパスを知る必要はなく、そのディレクトリファイル内のディレクトリエントリを読み取って、相対パス(および..
その点では他のファイルと同じです)そしてそこから続きます。
ここで、ユーザーとして、そのディレクトリがディレクトリツリーのどこにあるかを知りたい場合があります。ほとんどのUnicesでは、ディレクトリツリーはループのないツリーです。つまり、ツリーのルートからのパスは1つだけです(/
)任意のファイルに。そのパスは一般に正規パスと呼ばれます。
現在の作業ディレクトリのパスを取得するには、プロセスが実行する必要があるのは、上に移動するだけです(下に移動 ルートが下部にあるツリーを表示したい場合は、ツリーをルートに戻し、途中でノードの名前を見つけます。
たとえば、現在のディレクトリが/a/b/c
であることを確認しようとするプロセス 、..
を開きます ディレクトリ(相対パス、つまり..
は現在のディレクトリのエントリです)、.
、そのc
を見つけます 一致し、../..
を開きます /
が見つかるまで続きます 。あいまいさはありません。
それがgetwd()
またはgetcwd()
C関数は実行するか、少なくとも実行に使用されます。
最新のLinuxなどの一部のシステムでは、カーネル空間でルックアップを実行する現在のディレクトリへの正規パスを返すシステムコールがあります(すべてのコンポーネントへの読み取りアクセス権がない場合でも、現在のディレクトリを見つけることができます) 、それがgetcwd()
そこに電話します。最新のLinuxでは、/proc/self/cwd
のreadlink()を介して現在のディレクトリへのパスを見つけることもできます。 。
これは、ほとんどの言語と初期のシェルが現在のディレクトリにパスを返すときに行うことです。
あなたの場合、cd a
を呼び出すことができます .
、現在のディレクトリは変更されないため、すべてのgetcwd()
、pwd -P
、python -c 'import os; print os.getcwd()'
、perl -MPOSIX -le 'print getcwd'
${HOME}
を返します 。
さて、シンボリックリンクはそれをすべて複雑にしました。
symlinks
ディレクトリツリーでのジャンプを許可します。 /a/b/c
内 、/a
の場合 または/a/b
または/a/b/c
はシンボリックリンクであり、/a/b/c
の正規パス まったく違うものになります。特に、..
/a/b/c
のエントリ 必ずしも/a/b
である必要はありません 。
Bourneシェルでは、次の場合:
cd /a/b/c
cd ..
または:
cd /a/b/c/..
最終的に/a/b
になる保証はありません 。
のように:
vi /a/b/c/../d
必ずしも同じではありません:
vi /a/b/d
ksh
論理的な現在の作業ディレクトリの概念を導入しました どういうわけかそれを回避します。人々はそれに慣れ、POSIXはその動作を指定することになりました。これは、最近のほとんどのシェルでも同様に動作することを意味します。
cd
の場合 およびpwd
組み込みコマンド(そしてそれらのためだけに (ただし、popd
の場合も同様です / pushd
それらを持っているシェルでは))、シェルは現在の作業ディレクトリの独自の考えを維持します。 $PWD
に保存されています 特別な変数。
行う場合:
cd c/d
c
であっても またはc/d
$PWD
はシンボリックリンクですが、 /a/b
が含まれています 、c/d
を追加します 最後まで$PWD
/a/b/c/d
になります 。そしてあなたがそうするとき:
cd ../e
chdir("../e")
を実行する代わりに 、chdir("/a/b/c/e")
を実行します 。
そしてpwd
コマンドは、$PWD
のコンテンツのみを返します 変数。
pwd
であるため、インタラクティブシェルで役立ちます。 ..
のみを使用している限り、どのようにしてそこに到達したかに関する情報を提供する現在のディレクトリへのパスを出力します。 cd
への引数 cd a; cd ..
またはcd a/..
通常、元の場所に戻ります。
さて、$PWD
cd
を実行しない限り、変更されません 。次回cd
に電話するまで またはpwd
、多くのことが起こる可能性があります。$PWD
のコンポーネントのいずれかです。 名前を変更できます。現在のディレクトリは変更されませんが(削除される可能性はありますが、常に同じiノードです)、ディレクトリツリー内のパスが完全に変更される可能性があります。 getcwd()
ディレクトリツリーをたどって呼び出されるたびに現在のディレクトリを計算するため、その情報は常に正確ですが、POSIXシェルによって実装された論理ディレクトリの場合、$PWD
の情報 古くなる可能性があります。したがって、cd
を実行すると またはpwd
、一部のシェルはそれを防ぎたい場合があります。
その特定の例では、さまざまなシェルでさまざまな動作が見られます。
ksh93
のようなもの 問題を完全に無視するため、cd
を呼び出した後でも誤った情報が返されます (そして、bash
で見ているような動作は見られません。 そこに)。
bash
のようなもの またはzsh
$PWD
を確認してください cd
の現在のディレクトリへのパスのままです 、ただしpwd
ではありません 。
pdkshは両方のpwd
をチェックします およびcd
(ただし、pwd
、$PWD
を更新しません )
ash
(少なくともDebianで見つかったもの)はチェックせず、cd a
を実行すると 、実際にはcd "$PWD/a"
、したがって、現在のディレクトリが変更された場合、$PWD
現在のディレクトリを指さなくなり、実際にはa
に変更されません 現在のディレクトリにあるが、$PWD
にあるディレクトリ (存在しない場合はエラーを返します)。
それで遊びたい場合は、次のことができます:
cd
mkdir -p a/b
cd a
pwd
mv ~/a ~/b
pwd
echo "$PWD"
cd b
pwd; echo "$PWD"; pwd -P # (and notice the bug in ksh93)
さまざまなシェルで。
あなたの場合、bash
を使用しているので 、cd a
の後 、bash
$PWD
をチェックします まだ現在のディレクトリを指しています。そのために、stat()
を呼び出します。 $PWD
の値について iノード番号を確認して.
。
しかし、$PWD
を検索するとき パスには、stat()
という非常に多くのシンボリックリンクの解決が含まれます エラーで返されるため、シェルは$PWD
かどうかを確認できません まだ現在のディレクトリに対応しているため、getcwd()
を使用して再度計算します。 $PWD
を更新します それに応じて。
ここで、Patriceの答えを明確にするために、パスの検索中に検出されたシンボリックリンクの数をチェックすることは、シンボリックリンクのループを防ぐことです。最も単純なループは
で作成できますrm -f a b
ln -s a b
ln -s b a
そのセーフガードなしで、cd a/x
、システムはa
の場所を見つける必要があります にリンクし、b
を見つけます a
にリンクするシンボリックリンクです 、そしてそれは無期限に続くでしょう。これを防ぐ最も簡単な方法は、任意の数を超えるシンボリックリンクを解決した後であきらめることです。
ここで、論理的な現在の作業ディレクトリに戻ります。 なぜそれがそれほど良い機能ではないのか。 cd
専用であることを理解することが重要です シェル内であり、他のコマンドではありません。
例:
cd -- "$dir" && vi -- "$file"
常に同じとは限りません:
vi -- "$dir/$file"
そのため、常にcd -P
を使用することをお勧めすることがあります。 混乱を避けるためにスクリプトで(../x
の引数をソフトウェアで処理しないようにします 他の言語ではなくシェルで記述されているという理由だけで、他のコマンドとは異なります。
-P
オプションは、論理ディレクトリを無効にすることです。 cd -P -- "$var"
を処理します 実際にはchdir()
を呼び出します $var
のコンテンツについて (少なくとも$CDPATH
である限り 設定されていません。ただし、$var
の場合を除きます。 -
です (またはおそらく-2
、+3
…一部のシェルでは)しかし、それは別の話です)。そしてcd -P
の後 、$PWD
正規のパスが含まれます。