環境
$ bash --version
bash --version
GNU bash, version 4.4.20(1)-release (x86_64-pc-linux-gnu)
事象
$ mkdir test; cd test;
$ rmdir ../test <- カレントディレクトリを削除
$ cd ./
cd: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
$ echo $?; pwd
0 <- エラーメッセージが出力されるが成功している
/home/user42/test/./ <- pwdがおかしくなっている
$ cd ././/////./././/////././././///
cd: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
$ echo $?; pwd
0
/home/user42/test/./././/////./././/////././././/// <- !?!?!?
$ cd ../
$ echo $?; pwd
0
/home/user42 <- cd ../すると元に戻る
理由
bashは入力されたパスへの移動に失敗すると、入力値を直接引数としてcdに再挑戦するから。
調査
bashのソースは公式サイトからダウンロードできるので、読んでみました。
builtins/cd.defの中でcdの動作が定義されています。
実際にchdir(カレントディレクトリを変更する関数)を呼び出しているchange_to_directoryの処理をのぞいてみると、このようなコメントがあり、上記の動作が起こる理由が説明されています。
/* We're not in physical mode (nolinks == 0), but we failed to change to
the canonicalized directory name (TDIR). Try what the user passed
verbatim. If we succeed, reinitialize the_current_working_directory.
POSIX requires that we just fail here, so we do in posix mode. */
POSIXモード1でない際、正規化2したパス(TDIR)3へのcdが失敗すると、引数をそのままcdで試し、成功した場合、the_current_working_directory4の再設定を行うと記述されています。
cd ./を実行するとchdir("pwdの出力 + ./")に失敗した後、chdir("./")に挑戦しているようです。
bashはcdが一度失敗しても引数そのままのcdに挑戦してくれる親切設計なんですね。
終了ステータス
存在しないディレクトリでcd ./を実行すると、chdir(TDIR)2は失敗するが、chdir("./")は成功するので、cdの終了ステータスは0に設定されます。
また、存在しないディレクトリでのchdir("../")も成功します。
コメントに記述されているようにPOSIXモードだと失敗しました。
bash --posix <-bashをPOSIXモードで起動
$ mkdir test; cd test;
$ rmdir ../test <-カレントディレクトリを削除
$ cd ./
bash: cd: ./: No such file or directory
$ echo $?; pwd
1 <-cdの終了ステータスが違う
pwd: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
エラーメッセージ
存在しないディレクトリでcd ./を実行すると、下記のようなエラーメッセージが出力されます。
cd: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
このメッセージを吐き出しているのは、the_current_working_directory4の再設定を行う際に呼び出されるget_working_directoryという関数です。
関数内部でgetcwd(カレントディレクトリのパスの取得)を実行し、存在しないディレクトリにいることが原因で失敗するので、エラーメッセージが出力されます。
pwdの出力
存在しないディレクトリでcd ./を実行した後、pwdを実行すると、.や/がふくまれたパスを出力されます。
これは、get_working_directoryが失敗し、カレントディレクトリが取得できていないことが原因です。
カレントディレクトリが取得できておらず、the_current_working_directoryが設定できていないため、TDIR3がthe_current_working_directoryとして設定されています。
$ pwd
/home/user42/test/./././/////./././/////././././///
getcwdが成功するディレクトリに来たタイミングでthe_current_working_directoryは正しい値に修正されます。
$ pwd
/home/user42/test/./././/////././././
$ cd ../
$ pwd
/home/user42
まとめ
存在しないディレクトリでのcd ./成功は、bashの親切設計が引き起こしている挙動でした。
エラーメッセージは、getcwdが失敗したことによる出力なので、終了ステータスには影響がないようです。