🐔

環境変数 PATH に空文字があるとカレントディレクトリが指定されているのと同じ意味になる

1 min read 2

何だと!?

タイトルのまんま。
恥ずかしながらこれ今まで知らなかった。
もしかして常識だったりする?

ちなみに確認したのは Linux だけど、glibc の posix サブディレクトリ配下の execl*execvpe の挙動なので glibc 使ってればみんな一緒じゃないかな?知らんけど…

環境変数 PATH が設定されていない時

unset PATH とした状態。
この場合、PATH=/bin:/usr/bin と設定されているのと同じ意味になる(追記も参照のこと)。
当然カレントディレクトリは含まれていない

環境変数 PATH が空の時

PATH= とした状態。
PATH 環境変数が設定されていない時」とは異なる事に注意。
この場合、PATH=. と設定されているのと同じ意味になる。
つまり、カレントディレクトリが含まれている

環境変数 PATH の最後が : で終わっている場合

例えば、PATH=/bin:/usr/bin: となっている状態。
この場合、PATH=/bin:/usr/bin:. と設定されているのと同じ意味になる。
つまり、PATH の最後にカレントディレクトリが含まれている

環境変数 PATH の先頭が : から始まっている場合

例えば、PATH=:/bin:/usr/bin となっている状態。
この場合、PATH=.:/bin:/usr/bin と設定されているのと同じ意味になる。
つまり、PATH の最初にカレントディレクトリが含まれている

環境変数 PATH の途中に :: が含まれる場合

例えば、PATH=/bin::/usr/bin となっている状態。
この場合、PATH=/bin:.:/usr/bin と設定されているのと同じ意味になる。
つまり、PATH の途中にカレントディレクトリが含まれている

これって結構怖くね?

基本的にはカレントディレクトリを PATH に追加することは無いんだが、間違って : で始まったり : で終わったりは結構やりそう(今までもやってそう)な気がする…

2021/9/26追記

PATH が設定されていない時に PATH=/bin:/usr/bin と同じ意味になるのはあくまでも exec[vl]p* の挙動で、シェルから実行した場合の挙動はシェルの種類によるようだ。(shela_ さん、ko1nksm さんコメントありがとうございます)

https://twitter.com/shela_/status/1441745604086878208?conversation=none

ちなみに、env -u PATH コマンド とやると execvp を使ってるっぽいので上記の記載通り PATH=/bin:/usr/bin と同等になる。

よくよく考えれば、シェルは外部コマンドを実行する際にはコマンドのフルパスをキャッシュするので、PATH を自力で解決しているはずなので、exec[vl]p* の挙動と同じになるとは限らないな。

そう考えると、空文字の場合の挙動もシェルによって違っていておかしくない気はするが、どうもそこは統一されているようだ。

Discussion

環境変数 PATH が未定義もしくは空文字の場合の挙動は、おそらくシェル依存の話だと思ったので確認してみました。確認したシェルは dash、bash、ksh、mksh、yash、posh、zsh、busybox ash でいずれも Debian 10 の標準パッケージに含まれているものです。

  • unset PATH のとき
    • dash, bash, busybox ash: /bin:/usr/bin ではなくカレントディレクトリを参照します
    • yash, zsh: /bin:/usr/bin もカレントディレクトリも参照しません
    • ksh, mksh, posh: /bin:/usr/bin を参照しているようです
      (その他のディレクトリを参照するかどうかは未確認です)
  • PATH="" のとき
    • ksh: /bin:/usr/bin もカレントディレクトリも参照しません
    • 上記以外: カレントディレクトリを参照するようです


シェルで PATH を未定義または空文字にしてからシェルスクリプトを実行した場合=シェルスクリプトの中で起動時に PATH が未定義または空文字であった場合に PATH が初期化されます。Debian 環境では以下のように初期化されましたがおそらくシステムによって違うのではないかと思っています。

  • PATH が未定義(unset PATH)の場合
    • dash: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    • bash: /usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:.
    • ksh: 未定義
    • mksh: /usr/bin:/bin
    • posh: /bin:/usr/bin
    • yash: 未定義
    • zsh: /bin:/usr/bin:/usr/ucb:/usr/local/bin
    • busybox ash: /sbin:/usr/sbin:/bin:/usr/bin
  • PATH が空文字(export PATH="")の場合
    • 下記以外: 空文字
    • zsh: /usr/local/bin:/usr/bin:/bin:/usr/games

1984 年にベル研から出ている The UNIX Programming Environment という本にも記述があります.

You can omit the ‘.’; a null component in PATH means the current directory.
(p. 37)

さらに,この本では全体に渡ってカレントディレクトリが PATH に含まれている環境を想定しているようです. こんな問題も出ていました.

Exercise 3-13. Why do we always include the current directory in PATH? Where should it be placed?
(p. 92)

ログインするとコメントできます