はじめに
本記事のサンプルコードの環境はZshを用いています。
$ node
zsh: command not found: node
Node.js をインストールしたはずなのに、node
が使えないなあ。
このようなエラーに遭遇し、ネットで調べたところ「パスを通してください」という謎の助言を見かけたことがある人は少なくないと思います。
「パスを通す」ってなに?
結論ですが、「パスを通す」とは環境変数 PATH
にディレクトリを追加することです。
なぜそんなことをする必要があるのでしょうか?
コマンド実行の仕組み
例えば ls
や python
のようなコマンドを実行すると、シェルはそれに対応する 実行ファイル(プログラム) を探しにいきます。
そもそもプログラムを実行するだけなので、もちろん 絶対パスで直接実行することも可能 です。
$ /bin/ls
$ /opt/homebrew/bin/python # brewでインストールした場合
ですが、毎回このようにフルパスで書くのは面倒くさいですよね。
環境変数PATH
とは
そこで PATH
が活躍します。
PATH
は、シェルがコマンドを探す際に「このフォルダの中を探してくださいね」と決めておくディレクトリの一覧です。
実際に中身を覗いてみましょう。
$ echo $PATH
/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin # あくまで一例です。通常ここまで少ないことはないと思います。
:
で区切られて複数のディレクトリのパスが並んでいます。
上記の場合、これらがどのように機能しているのでしょうか。
例えば python
と入力されると、シェルは次のような順序でプログラムの本体を探しに行きます。
-
/opt/homebrew/bin/
にあるか? - なければ
/usr/local/bin/
は? - それでもなければ
/usr/bin/
と、順番に確認
そして、 最初に見つけたプログラムを実行 します。
ちなみに、コマンドの実行ファイルがどこにあるのか確認したい場合は、以下のように確かめることができます。(パスが通っていれば見つかります)
$ which python
/opt/homebrew/bin/python
上記の場合、/opt/homebrew/bin/python
にpython
の実行ファイルが設置されていることがわかります。
もちろん、これは絶対パスの指定で実行することも可能です。
PATH
以外は探さない
重要なのは、PATH
に登録された場所以外は探してくれないということです。
以下のケースを考えてみます。
$ echo $PATH
/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin
$ ls ~/.scripts
dairy_script.sh
実行したいプログラムが ~/.scripts/dairy_script.sh
だとします。
しかしながら、 $PATH
には /opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin
しか登録されていません。
プログラムを探す場所として .scripts
ディレクトリが登録されていないため、 dairy_script.sh
と実行しても当然プログラムが見つからずエラーになります。
$ dairy_script.sh
zsh: command not found: dairy_script.sh
PATH
を追加する意味
「パスを通す」とは
プログラムが存在する場所を
PATH
に追加して、シェルが探せるようにすること
なわけですね。
ですので、パスを通してプログラムの場所を探せるようにしてあげれば、どこからでも簡単に呼び出すことが可能になります。
$ export PATH="$HOME/Desktop/scripts:$PATH"
$ dairy_scripts.sh
export
の落とし穴
先ほど紹介したように、 export
で PATH
を更新すればどこからでも呼び出せるようになりますが、実はこれは 一時的にしか動作しません。
ターミナルを閉じるとリセットされる
export
コマンドで設定した環境変数の変更は、ターミナルを閉じたり再起動すると、綺麗さっぱり消滅します。
ターミナルを起動するたびに毎回手動で export
するのは現実的ではないですよね。
毎回設定するには?
その手間を省くためには、 シェルが起動時に読み込む設定ファイルに PATH の追加を書いておく のが一般的です。
使用しているシェルによって、読み込まれる設定ファイルは異なります。(基本的に役割は同じです)
シェル | 設定ファイル |
---|---|
Bash |
~/.bash_profile , ~/.bashrc
|
Zsh | ~/.zshrc |
Fish | ~/.config/fish/config.fish |
たとえば zsh を使っている場合、 ~/.zshrc
に以下のように書いておけば、起動するたびに設定してある内容が反映されます。
# ~/.zshrc
export PATH="$HOME/Desktop/scripts:$PATH"
プログラムを探す順番
補足ですが、シェルはコマンドを実行する際に、 PATH
に登録されたディレクトリを 左から順番に 探していきます。
たとえば以下のような PATH
が設定されているとします。
$ echo $PATH
/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin
先ほども少し触れましたが、例えばこの状態で python
を実行すると、次のような順番でプログラムを探します。
-
/opt/homebrew/bin/python
を確認 - 見つからなければ
/usr/local/bin/python
を確認 - 見つからなければ
/usr/bin/python
を確認 - 見つからなければ
/bin/python
を確認 - この中で最初に見つかったものを実行する
- 全てのディレクトリで見つからなければ
command not found
になる
したがって /usr/bin/python
と /opt/homebrew/bin/python
の両方に存在していた場合、先に見つかる /opt/homebrew/bin/python
の方が実行されるわけです。
(発展)ビルトインコマンドと外部コマンド
コマンドは PATH
の中から探し、実行ファイルが見つかったものが実行されるという話をしました。
ですが、実際はすべてのコマンドが PATH
から探されているわけではありません。
普段利用する cd
や pwd
など、パスを通した覚えはありませんよね。
実は、コマンドには 「ビルトインコマンド」と「外部コマンド」 の 2 種類があり、 PATH から探されるのは外部コマンドだけ なんです。
ビルトインコマンドとは?
ビルトインコマンドは、 シェルにあらかじめ組み込まれているコマンド です。
外部にプログラムが存在しているわけではなく、シェルの内部で実装されています。
興味があれば、Bash のソースコードを見ていただくと、コマンドがどのように実装されているのかがわかります。
例として、以下のコマンド群はビルトインコマンドです。(一部の抜粋です)
cd
echo
exit
どうやって調べるの?
type
でコマンドの種類を判別できます。
$ type cd
cd is a shell builtin
cd
がビルトインコマンドであることがわかりますね。
ビルトインコマンドは 外部のプログラムとして存在していないため、PATH の影響を受けません。
「パスを通す」といった操作とは無関係で、何もしていなくとも使えるコマンドというわけです。
外部コマンドとは?
-
ls
,node
,python
,git
など - 外部に実行ファイルが存在
- 実行時は
PATH
を見て探す
$ type node
node is /opt/homebrew/bin/node
こちらはお話している通り、外部にプログラムが置いてありますので、 PATH
を通す必要があります。
おわりに
「パスを通す」とはどういうことなのか、色々な角度からお話しました。
これでもう「パスを通してね」と言われても、「はいはい、PATH ね」とすんなり理解できるようになったかと思います。