Symlinkの再帰 - 「リセット」になるのはなぜですか?

Symlinkの再帰 - 「リセット」になるのはなぜですか?

同じディレクトリを指すシンボリックリンクに続くときに何が起こるかを確認するために、小さな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

ここで何が起こっているのでしょうか?

ベストアンサー1

Patrice は問題の原因を特定します。彼の答えしかし、そこからどのように到達するのか、なぜこれを得るのか疑問に思うなら、物語は長くなります。

プロセスの現在の作業ディレクトリは複雑ではありません。これはプロセスの属性であり、相対パス(プロセスによって実行されたシステムコール)から始まるディレクトリタイプファイルへのハンドルです。相対パスを確認するとき、カーネルは現在のディレクトリのフルパスを知る必要はなく、単にそのディレクトリのファイルからディレクトリエントリを読み取り、相対パスの最初のコンポーネント(..他のファイルと同様)を見つけてそこに進みます。

これで、ユーザーはディレクトリツリーでこのディレクトリがどこにあるかを知りたい場合があります。ほとんどのUnicesの場合、ディレクトリツリーはループのない単一のツリーです。つまり、/ツリーのルート()から特定のファイルへのパスは1つだけです。この経路をしばしば標準経路と呼びます。

現在の作業ディレクトリへのパスを取得するには、プロセスが何をすべきかは上に戻るだけです。下に根が下にある木を見たい場合は、木を根に戻して途中のノードの名前を見つけます。

たとえば、現在のディレクトリが何であるかを調べるプロセスは、そのディレクトリ(現在のディレクトリのエントリと同じ相対パス)を/a/b/c開き、同じinode番号を持つディレクトリタイプファイルを見つけて、一致するエントリを見つけてそれを開きます。そして、あなたがそれを見つけるまで続きます。そこにあいまいさはありません。.....c../../

これがC関数がするgetwd()ことgetcwd()、または少なくとも以前はやったことです。

一部のシステム(最新のLinuxなど)には、カーネル空間で検索されている現在のディレクトリのフルパスを返すシステムコールがあります。すべてのエントリに対する読み取りアクセス権がない場合でも、現在のディレクトリを見つけることができます。そのコンポーネント)、これをいいますgetcwd()。最新のLinuxでは、readlink()を介して現在のディレクトリへのパスを見つけることもできます/proc/self/cwd

これは、ほとんどの言語と初期シェルが現在のディレクトリへのパスを返すときに実行する操作です。

あなたの場合は、好きcd aなだけ何度でも呼び出すことができます。へのシンボリックリンクであるため、.現在のディレクトリは変更されないため、すべての、、がgetcwd()あなたに返されます。pwd -Ppython -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は最終的にこの動作を指定しました。つまり、ほとんどのシェルもこの動作を行います。

と組み込みコマンドcdpwdそして彼らだけのためにpopd(これは/を持つシェルにも当てはまりますがpushd))シェルは現在の作業ディレクトリに対する独自のアイデアを保持します。$PWD特殊変数に保存されます。

これを行うとき:

cd c/d

cor が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多くのことが発生する可能性があります。現在のディレクトリは絶対に変更されていませんが(削除可能でも常に同じインデックスノードです)、ディレクトリツリーの対応するパスは完全に変更できます。現在のディレクトリは、呼び出されるたびにディレクトリツリーを参照して計算されるため、その情報は常に正確ですが、POSIXシェルで実装されている論理ディレクトリの場合、情報は最新ではない可能性があります。したがって、一部のシェルでは、または。cdpwd$PWDgetcwd()$PWDcdpwd

この特定のインスタンスでは、異なるシェルで異なる動作を見ることができます。

一部の人々はksh93問題を完全に無視するのが好きなので、問題を呼び出した後も間違った情報が返されますcd(そしてそこに現れる動作を見ることができませんbash)。

一部の人は、それがまだ現在のディレクトリへのパスであることを確認するのが好きかどうかを確認しますbashzsh、そうではありません。$PWDcdpwd

pwdpdkshは合計を確認しますcd(ただし、pwd更新は確認しません$PWD)。

ash(少なくともDebianでは)確認しません。これによりcd a実際に確認しますcd "$PWD/a"。したがって、現在のディレクトリが変更されて現在のディレクトリを指していない場合、実際には現在のディレクトリ内のディレクトリ$PWDに変更されません。aディレクトリの1つです$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あなたの場合はa以降に使用しているので、それでも現在のディレクトリを指してcd aいることをbash確認してください。$PWDこれを行うには、stat()の値を呼び出して$PWDinode番号を確認し、それをの値と比較します.

ただし、$PWDルックアップパスにあまりにも多くのシンボリックリンクが含まれているとエラーが返されるため、stat()シェルは$PWD現在のディレクトリに対応していることを確認できないため、再計算してgetcwd()それに応じて更新します$PWD

パトリスの答えを明確にするために、パスを見つけるときに見つかったシンボリックリンクの数を確認することは、シンボリックリンクサイクルを防ぐことです。最も簡単なループを使用できます

rm -f a b
ln -s a b
ln -s b a

これらの保護装置がない場合、Windowsでは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+3cd -P$PWD

おすすめ記事