はじめに
環境構築で必ずぶちあたる壁の1つが「パスが通らない」ことです。Command not found
とシェルに怒られるたびに、そこらへんに転がってる記事に書いてある内容を意味もわからずにそのまま実行してる人も多いんじゃないでしょうか。
この記事を読んで、今日こそ『パスが通らない!!』から完全におさらばしましょう!
環境変数
記事に書いてあるexport PATH=/foo/bar:$PATH
を意味もわからずに実行してませんか?まずはこの式の意味を理解しましょう。
環境変数とは
環境変数とは、OSが保持するKey-Value型のデータのことです。OSが保持しているので、異なるプロセス間で同じデータを共有することができます。
操作
まずは軽く環境変数を操作してみて、環境変数について理解を深めましょう。
作成
試しに~
という値を持つ環境変数example
を作成してみます。
$ export example=~
以下はどちらも同じ出力結果になります。
$ echo ~
$ echo $example
環境変数の名前を大文字にするか小文字にするかですが、以下の記事が参考になります。今回の例では小文字を使う方が適切なようです。
慣習的には、他のアプリケーションやユーザーに設定させて引き継ぐことを意図する環境変数には大文字を使い、それ以外には小文字を使うことが多いようです。
表示
以下のようにして、指定した環境変数に設定されている値を表示できます。
$ echo $example
ちなみに以下のように実行すると、環境変数の一覧が表示されます。
$ export -p
...
declare -x example="/home/ubuntu"
...
上書き
再度値を設定すると、元の値を上書きできます。
$ export example=Hello
$ echo $example
Hello
以下のように元の値を残しつつ、デリミタ(区切り文字)を使って文字列を追加することもできます。環境変数においては、:
(コロン)がデリミタになります。
$ export example=$example:World
$ echo $example
Hello:World
$ export example=x:$example
$ echo $example
x:Hello:World
つまり、export PATH=/foo/bar:$PATH
というのは、既存の環境変数PATH
の値の先頭に/foo/bar:
という文字列を追加するという意味になります。環境変数PATH
自体については後述します。
ちなみに下記のように、間にダブルクオーテーションを2つ挟めば、デリミタを使わずに文字列を追加できます。
$ export example=$example""!
$ echo $example
x:Hello:world!
削除
下記のようにして環境変数を削除できます。
$ unset example
$ echo $example
$PATH
とは?
お次に$PATH
について理解しましょう。$PATH
は環境変数の1つだということは前述したとおりですが、どのように使われるものなのでしょうか?
コマンド検索パス
結論から言うと、$PATH
の値はコマンド検索パスになります。コマンド検索パスとは、シェルがコマンドの実行ファイルを探しに行くパスのことです。この説明だけだとよくわからないですね。もう少し詳しく説明します。
Linuxコマンドの1つにls
コマンドがあると思います。試しにls
コマンドを実行してみましょう。カレントディレクトリにあるファイルの一覧が表示されると思います。
$ ls
次に、以下のように実行してみてください。環境によってwhich ls
の実行結果が違ってくるとは思いますが、適宜読み替えてください。
$ which ls
/bin/ls
which
は指定したコマンドの実行ファイルのパスを表示してくれるコマンドです。ls
というコマンドの実行ファイル(プログラム)が/bin/ls
という場所にあることがわかります。試しにls /bin
と実行してみてください。ls
というファイルが存在することがわかると思います(他にも見覚えのある名前がいくつかあるでしょう)。
つまり、今までコマンドとして実行していたls
は、シェルによって/bin/ls
と読み替えられて実行されていたのです(その証拠に/bin/ls
と実行するとls
と同じ実行結果が得られます)。ではなぜシェルはls
= /bin/ls
と解釈することができたのでしょうか?
その答えがコマンド検索パスになります。$PATH
の持つ値を見てみましょう。
$ echo $PATH
...:/bin...
見づらいですが、複数のパスが:
で区切られて表示されており(前述しました)、その中に:bin
があると思います。
つまり、ls
が実行されると、シェルはまずカレントディレクトリ内にls
という実行ファイルが存在するかを確認し、なければ$PATH
の持つ値(コマンド検索パス)を確認して、それらのパスが指し示す場所にls
という実行ファイルが存在するかを1つずつ確認していきます。今回の場合は、/bin
の下にls
が存在するので、シェルは/bin/ls
を実行するわけです。
ちなみに、$PATH
の値が...:/usr/bin:/bin...
となっていて、/usr/bin
の下にもls
が存在する場合は、シェルは/usr/bin/ls
を実行します。先に書いてあるパスが優先して使われるわけです。
「パスが通らない」とは
つまり、「パスが通らない」とは、シェルに実行ファイルまでのパスを渡せてないということです。もっと詳しくいうと、$PATH
に実行ファイルが存在するディレクトリのパスが書かれていないということになります。
なので「パスを通す」には、$PATH
に実行ファイルが存在するのパスを追記してあげればいいわけです。追記するのは先頭と後ろのどちらでもいいとは思いますが、万が一同じ名前の実行ファイルがあった場合のために先頭に追記してあげることが多いです。
$ export PATH=/foo/bar:$PATH
設定ファイル
さてこれでパスを通せるようになるぞと思った人もいるかもしれませんが、export PATH=/foo/bar:$PATH
を実行してパスを追加するだけでは不十分です。
というのも、シェルでexport PATH=/foo/bar:$PATH
を実行して環境変数の値を設定しても、その設定が持続するのはそのセッションの間だけで、シェルを閉じてしまうと、設定した内容が全て消えてしまいます。
恒久的にその設定を適用し続けるには、シェルでexport PATH=/foo/bar:$PATH
を実行するのではなく、シェルの設定ファイルにexport PATH=/foo/bar:$PATH
を書き込む必要があります。
シェルごとの設定ファイルの種類
シェルには、bash、zsh、shなどがあります。それぞれのシェルごとに使用する設定ファイルが異なりますので、それぞれの使用する設定ファイルについて詳しく見ていきましょう。
bash
デフォルトのシェルをbashにしている場合は、以下の設定ファイルが順番に読み込まれます。全て実態はシェルスクリプトなので、単なる設定だけでなく、処理なども記述することができます。
/etc/profile
~/.bash_profile
~/.bash_login
~/.profile
~/.bashrc
~/.bash_logout
/etc/profile
全ユーザーに適用されるデフォルトの設定ファイルです。ユーザー単位では編集する必要はないので、ほとんどいじることはないと思います。ログイン時に全ユーザーに適用したい設定や処理がある場合に使います。
~/.bash_profile
あればログイン時に読み込まれます。無くても問題ありません。ユーザー単位の設定を記述します。ログイン時に一度だけ読み込めばいい設定や処理のみを記述し、余計なものは極力書かない方がいいようです。
環境変数はプロセス間で受け継がれるので、ログイン時のみ読み込めればOKなため、環境変数を記述するのによく使われます。なので、「パスを通す」ためには、このファイルにexport PATH=/foo/bar:$PATH
を追記することで、恒久的にパスを通すことができるようになります。
~/.bash_login
~/.bash_profile
が存在しない場合にのみ、あればログイン時に読み込まれます。無くても問題ありません。
~/.profile
~/.bash_profile
と~/.bash_login
の両方が存在しない場合にのみ、あればログイン時に読み込まれます。無くても問題ありません。
特定のシェルに依存しない設定ファイルなので、bashに依存しない環境変数や、GUIで使う設定を書くのに使われます。
~/.bashrc
あれば対話モードのbashを起動するときに読み込まれます。無くても問題ありません。
環境変数でない変数や、エイリアス、シェル関数、コマンドライン補完の設定、シェルオプション、プロンプト設定、EDITOR
変数などのbashに依存する対話モード向けの設定や処理を書くのに使います。
逆に言うと、対話モードのbashを使用するときにのみ適用されるので、環境変数をここに書くのは適切ではありません(GUIや対話モードのbash以外から起動するプログラムなどに環境変数が渡らなくなるため)。
また、rsyncなどのsshパイプで問題が生じることがあるため、~/.bashrc
に、標準出力や標準エラーに何かが出力されるような処理を書いてはいけないらしいです。
~/.bash_logout
あればログアウト時に読み込まれます。無くても問題ありません。
zsh
途中
sh
途中
設定ファイルにexport PATH=/foo/bar:$PATH
を追記する
bash
前述したように、bashで実行するコマンドのパスを通したいときは、~/.bash_profile
に、GUIやbash以外から起動するプログラムにて使用する場合は~/.profile
に、export PATH=/foo/bar:$PATH
を追記しましょう。
ただ、環境によって読み込まれる設定ファイルの順番などが異なる場合があります。一番確実なのは、~/.bashrc
と、~/.profile
、~/.bash_profile
、~/.bash_login
の3つのうちで存在するファイル全てに対して環境変数を記述することです(3つ全て存在しない場合は~/.profile
に記述してください)。
pyenvのドキュメントに記述されている内容を根拠にしています。ご指摘等ある方はこの記事のコメント欄にて教えていただけると幸いです。
Stock Bash startup files vary widely between distributions in which of them source which, under what circumstances, in what order and what additional configuration they perform. As such, the most reliable way to get Pyenv in all environments is to append Pyenv configuration commands to both .bashrc (for interactive shells) and the profile file that Bash would use (for login shells).
Then, if you have ~/.profile, ~/.bash_profile or ~/.bash_login, add the commands there as well. If you have none of these, add them to ~/.profile.
例えば、~/.bash_login
が存在しない場合は、~/.bashrc
、~/.profile
、~/.bash_profile
に環境変数を記述します。
$ echo "export PATH=/foo/bar:$PATH" >> ~/.bashrc
$ echo "export PATH=/foo/bar:$PATH" >> ~/.bash_profile
$ echo "export PATH=/foo/bar:$PATH" >> ~/.profile
ちなみにパスは絶対パスで指定する必要があります。
zsh
途中
sh
途中
設定ファイルの変更を適用させる
設定ファイルを変更したら、以下のコマンドを実行しないと変更が反映されません。
$ source <設定ファイルのパス>
または、以下のコマンドでも反映できます。複数のファイルを編集した場合は、上記のコマンドだとファイル数分実行しないといけないので、下記のコマンドの方が楽です。
$ exec $SHELL
その他参考文献