同じディレクトリを指すシンボリックリンクに続くときに何が起こるかを確認するために、小さな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 -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
or が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シェルで実装されている論理ディレクトリの場合、情報は最新ではない可能性があります。したがって、一部のシェルでは、または。cd
pwd
$PWD
getcwd()
$PWD
cd
pwd
この特定のインスタンスでは、異なるシェルで異なる動作を見ることができます。
一部の人々はksh93
問題を完全に無視するのが好きなので、問題を呼び出した後も間違った情報が返されますcd
(そしてそこに現れる動作を見ることができませんbash
)。
一部の人は、それがまだ現在のディレクトリへのパスであることを確認するのが好きかどうかを確認しますbash
がzsh
、そうではありません。$PWD
cd
pwd
pwd
pdkshは合計を確認します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()
の値を呼び出して$PWD
inode番号を確認し、それをの値と比較します.
。
ただし、$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
+3
cd -P
$PWD