やわらかテック

興味のあること。業務を通して得られた発見。個人的に試してみたことをアウトプットしています🍵

知ってるようで知らない$PATHについて調べてみた

プログラミングを始めたばかりの頃、Python3をインストールするべく環境構築に取り組んでいました。
その際によく見かけたのは「パス($PATH)を通す」という言葉です。当時の自分には何のことだか全く分からず「なんでプログラミングのインストールにバスケじゃあるまいし、パスが必要なんだろう...」と思ったものです。

今になっては、なぜパスを通す必要があるのか理解しているつもりです。
しかし、実際にどのように環境変数$PATHが使われているのかまでは知りません。 何かしらのコマンドをインストールする際にどうしても流れ作業的に$PATHを通しているので、あまり深く考えたことがありませんでした。

今回は環境変数$PATHとは何なのか・どのように使われているのか深掘ってみたいと思います。

$PATHに登録されている値を見てみる

まずは実際に$PATHにどのような値が格納されているのか見てみます。
先ほども書いたように$PATHは環境変数であり、ターミナルから簡単に参照することが可能です。

$ echo $PATH
/usr/local/bin:/System/Cryptexes/App/usr/bin: ...(省略)... /usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin:/Library/Apple/usr/bin

$PATHには複数の値が:区切りで格納されています。
見やすさのために:で区切って表示させてみると、思ったよりも多くの値が登録されていることに驚かされます。

/Users/xxxxxxxx/.rbenv/shims
/opt/homebrew/bin
/opt/homebrew/sbin
/usr/local/bin
/System/Cryptexes/App/usr/bin
:
/usr/bin
/bin
/usr/sbin
/sbin
/usr/local/go/bin
/Library/Apple/usr/bin

それぞれの値は何を示しているのでしょうか。
試しにfileコマンドを使って/user/binが何なのか見てみるとディレクトリであることが分かりました。
他の値も同様にディレクトリだったので、環境変数$PATHにはディレクトリのパスが:区切りで格納されていることが分かりました。

$ file /usr/bin
/usr/bin: directory

それぞれのディレクトリには何が入っているのか

ディレクトリへのパス情報ということは、該当のディレクトリは何かしらのファイルを含んでいるはずです。
一体、どのようなファイルが含まれているのでしょうか。先ほどと同じようにコマンドを使って確認してみます。

$ ls /usr/bin | head -n 5
AssetCacheLocatorUtil
AssetCacheManagerUtil
AssetCacheTetheratorUtil
DeRez
GetFileInfo

何やら見慣れないファイルが確認できました。
fileコマンドを使ってGetFileInfoについて、もう少し情報を確認してみます。

$ file /usr/bin/GetFileInfo
/usr/bin/GetFileInfo: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64e:Mach-O 64-bit executable arm64e]
/usr/bin/GetFileInfo (for architecture x86_64): Mach-O 64-bit executable x86_64
/usr/bin/GetFileInfo (for architecture arm64e): Mach-O 64-bit executable arm64e

...。
ガーッと情報が出力されていますが、要するにGetFileInfoは実行可能なバイナリーフォマットのファイルであることを示しています。 なのでターミナルでGetFileInfoと入力すれば、コマンドとして実行可能なはずです。

$ GetFileInfo
usage: GetFileInfo [-P] [-a[<attrib-letter>] | -t | -c | -d | -m] <path>

$ GetFileInfo index.ts
file: "/Users/xxxxxxx/sample/index.ts"
type: "\0\0\0\0"
creator: "\0\0\0\0"
attributes: avbstclinmedz
created: 09/12/2023 22:05:19
modified: 09/12/2023 22:05:19

問題なく実行できました。
さらに先ほどの/usr/binディレクトリには文字数の都合で省略しましたが、ちらほらと見慣れたファイル名も確認できました。 headというファイル名があったので、おそらくheadコマンドの実行ファイルだと考えられます。このコマンドをパスを指定して直接、呼び出してみるとどうなるでしょうか。

$ ls | /usr/bin/head -n 1
README.md

思った通りです。
つまり$PATHに記録されている、それぞれのディレクトリには「実行可能なファイル」が入っていることが分かりました。 後付け的な紹介になりますが、先ほど$PATHに格納されていた、いくつかのディレクトリには普段よく使うコマンドが含まれています。

  • /bin: 一般的なコマンド格納先(cat、rmなど)
  • /usr/bin: 一般ユーザ向けコマンド格納先(make、wgetなど)
  • /usr/sbin: 管理者向けコマンド格納先(chroot、useraddなど)
  • /usr/local/bin: 評価版パッケージや自作コマンドの格納先

Linuxコマンドのソースコードを取得する方法:オリジナルコマンド作成前の勉強向け #初心者 - Qiita

$PATHをいつ・どこで使っているのか

$PATHについて理解が深まりました。では実際に$PATHはどこで、いつ使われているのでしょうか。
ターミナルにechoと打ち込まれた後、コマンドが実行されるまでの流れを考えてみます。実際にターミナルの実装コードを見たわけではないので、あくまで推測です。

  • ターミナルに打ち込まれた文字を解析する
  • 解析結果より文字列echoを取得する
  • 環境変数$PATHを取得して:区切りで分割してディレクトリの一覧を取得する
  • 各ディレクトリの中を順に検索しechoに該当する実行可能ファイルを検索する
  • 一致した実行可能ファイルを実行する

推測ではありますが、どこからでもechoコマンドが使える仕組みは意外にもシンプルですね。
これが$PATHたる一つの環境変数によって実現されていると思うと、その効力・パワフルさを感じさせられます。
今回のテーマから少し外れてしまいますが$PATHに登録されている全てのディレクトリから該当のコマンドを探す際は、どのようなアルゴリズムが使われているのか気になります。登録されているディレクトリが多い場合など検索効率の悪化が起きそうなので、何かしらの工夫をしているはずです。

まとめ

今回は知っているようで知らない$PATHについて深掘ってみました。

  • $PATHにはディレクトリへのパス情報が:区切りで格納されている
  • それぞれのディレクトリは実行可能ファイルを含んでいる
  • ターミナルがコマンドを参照する時、$PATHの値から該当のコマンドを検索する

自分自身、裏側で何が起きているのか・どういう使われ方をしているかをしっかりと把握できていなかったので、実行可能ファイルの存在を確認して直接、実行できた時はテンションが上がりました。

あらゆる箇所からコマンドが使えるのは$PATHを使ったシンプルでパワフルな設計のおかげです。
少しでも「ええな〜」と思ったらはてなスター・はてなブックマーク・シェアを頂けると励みになります。