パトリスは彼の答えで問題の原因を特定しましたが、そこからどうやってそれを得るのかを知りたい場合は、ここに長い話があります。
プロセスの現在の作業ディレクトリは、複雑すぎるとは思わないでしょう。これは、プロセスの属性であり、相対パス(プロセスによって作成されたシステムコール内)が始まるディレクトリタイプのファイルへのハンドルです。相対パスを解決する場合、カーネルは(a)現在のディレクトリへのフルパスを知る必要はありません。そのディレクトリファイルのディレクトリエントリを読み取り、相対パスの最初のコンポーネントを見つけるだけです(..
他のコンポーネントと同様です)その点でファイル)とそこから続行します。
現在、ユーザーとして、ディレクトリツリー内のそのディレクトリの場所を知りたい場合があります。ほとんどのUnicesでは、ディレクトリツリーはループのないツリーです。つまり、ツリーのルート(/
)から特定のファイルへのパスは1つだけです。そのパスは、一般に標準パスと呼ばれます。
現在の作業ディレクトリのパスを取得するために、プロセスがしなければならないことは、ノードの名前を見つけるために、ルートに戻るツリーをたどるだけです(ルートが一番下にあるツリーを表示したい場合は十分に下降します)途中。
たとえば、現在のディレクトリが/a/b/c
であることを見つけようとするプロセスは、..
ディレクトリ(相対パス、..
つまり現在のディレクトリのエントリ)を開き、と同じinode番号を持つディレクトリタイプのファイルを.
探します。c
が一致してから開く../..
など、が見つかるまで続きます/
。あいまいさはありません。
それは、getwd()
またはgetcwd()
C関数が行うこと、または少なくとも以前は行っていたことです。
現代の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
またはc/d
がシンボリックリンクであっても、$PWD
containesの/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
、現在のディレクトリ内のディレクトリ、ただし、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
、の後にcd a
、まだ現在のディレクトリを指すbash
チェックがあり$PWD
ます。それを行うstat()
には、値を呼び出して$PWD
そのiノード番号を確認し、その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
であり、それは無期限に続きます。それを防ぐ最も簡単な方法は、任意の数を超えるシンボリックリンクを解決した後にgiveめることです。
論理的な現在の作業ディレクトリに戻り、なぜそれがあまり良い機能ではないのか。cd
シェル内でのみ使用され、他のコマンドではないことを理解することが重要です。
例えば:
cd -- "$dir" && vi -- "$file"
常に同じとは限りません:
vi -- "$dir/$file"
そのcd -P
ため、混乱を避けるために常にスクリプトで使用することを人々が推奨することがあります(../x
別の言語ではなくシェルで書かれているという理由だけで、ソフトウェアが他のコマンドとは異なる引数を処理するのは望ましくありません)。
-P
オプションは無効にすることで論理ディレクトリので、取り扱いcd -P -- "$var"
、実際に呼び出してんchdir()
の内容に$var
(場合を除き、$var
される-
が、それはまた別の話だが)。そして、後にcd -P
、$PWD
正規のパスが含まれます。