今日こそ『パスが通らない！！』から完全におさらばするための記事

# はじめに
環境構築で必ずぶちあたる壁の１つが「パスが通らない」ことです。`Command not found`とシェルに怒られるたびに、そこらへんに転がってる記事に書いてある内容を意味もわからずにそのまま実行してる人も多いんじゃないでしょうか。

この記事を読んで、今日こそ『パスが通らない！！』から完全におさらばしましょう！

# 環境変数
記事に書いてある`export PATH=/foo/bar:$PATH`を意味もわからずに実行してませんか？まずはこの式の意味を理解しましょう。

## 環境変数とは

環境変数とは、OSが保持するKey-Value型のデータのことです。OSが保持しているので、異なるプロセス間で同じデータを共有することができます。

https://ja.wikipedia.org/wiki/%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0

## 操作
まずは軽く環境変数を操作してみて、環境変数について理解を深めましょう。

### 作成

試しに`~`という値を持つ環境変数`example`を作成してみます。

```bash
$ export example=~
```
以下はどちらも同じ出力結果になります。
```bash
$ echo ~
$ echo $example
```

:::note info
環境変数の名前を大文字にするか小文字にするかですが、以下の記事が参考になります。今回の例では小文字を使う方が適切なようです。

> 慣習的には、他のアプリケーションやユーザーに設定させて引き継ぐことを意図する環境変数には大文字を使い、それ以外には小文字を使うことが多いようです。
>
> [大文字でない環境変数を利用することに問題はありますか - スタック・オーバーフロー](https://ja.stackoverflow.com/questions/48637/%E5%A4%A7%E6%96%87%E5%AD%97%E3%81%A7%E3%81%AA%E3%81%84%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0%E3%82%92%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%AB%E5%95%8F%E9%A1%8C%E3%81%AF%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%81%8B)
:::

### 表示
以下のようにして、指定した環境変数に設定されている値を表示できます。

```bash
$ echo $example
```

ちなみに以下のように実行すると、環境変数の一覧が表示されます。

```bash
$ export -p

...

declare -x example="/home/ubuntu"

...
```

### 上書き
再度値を設定すると、元の値を上書きできます。

```bash
$ export example=Hello
$ echo $example
Hello
```
以下のように元の値を残しつつ、デリミタ（区切り文字）を使って文字列を追加することもできます。環境変数においては、`:`（コロン）がデリミタになります。
```bash
$ 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`自体については後述します。

ちなみに下記のように、間にダブルクオーテーションを２つ挟めば、デリミタを使わずに文字列を追加できます。
```bash
$ export example=$example""!
$ echo $example
x:Hello:world!
```

https://blog.s-giken.net/446.html

### 削除
下記のようにして環境変数を削除できます。

```bash
$ unset example
$ echo $example

```

## `$PATH`とは？
お次に`$PATH`について理解しましょう。`$PATH`は環境変数の１つだということは前述したとおりですが、どのように使われるものなのでしょうか？

### コマンド検索パス
結論から言うと、`$PATH`の値はコマンド検索パスになります。コマンド検索パスとは、シェルがコマンドの実行ファイルを探しに行くパスのことです。この説明だけだとよくわからないですね。もう少し詳しく説明します。

Linuxコマンドの１つに`ls`コマンドがあると思います。試しに`ls`コマンドを実行してみましょう。カレントディレクトリにあるファイルの一覧が表示されると思います。
```bash
$ ls
```
次に、以下のように実行してみてください。環境によって`which ls`の実行結果が違ってくるとは思いますが、適宜読み替えてください。
```bash
$ which ls
/bin/ls
```
`which`は指定したコマンドの実行ファイルのパスを表示してくれるコマンドです。`ls`というコマンドの実行ファイル（プログラム）が`/bin/ls`という場所にあることがわかります。試しに`ls /bin`と実行してみてください。`ls`というファイルが存在することがわかると思います（他にも見覚えのある名前がいくつかあるでしょう）。

つまり、今までコマンドとして実行していた`ls`は、シェルによって`/bin/ls`と読み替えられて実行されていたのです（その証拠に`/bin/ls`と実行すると`ls`と同じ実行結果が得られます）。ではなぜシェルは`ls` = `/bin/ls`と解釈することができたのでしょうか？

その答えがコマンド検索パスになります。`$PATH`の持つ値を見てみましょう。
```bash
$ echo $PATH
...:/bin...
```
見づらいですが、複数のパスが`:`で区切られて表示されており（前述しました）、その中に`:bin`があると思います。

つまり、`ls`が実行されると、シェルはまずカレントディレクトリ内に`ls`という実行ファイルが存在するかを確認し、なければ`$PATH`の持つ値（コマンド検索パス）を確認して、それらのパスが指し示す場所に`ls`という実行ファイルが存在するかを１つずつ確認していきます。今回の場合は、`/bin`の下に`ls`が存在するので、シェルは`/bin/ls`を実行するわけです。

ちなみに、`$PATH`の値が`...:/usr/bin:/bin...`となっていて、`/usr/bin`の下にも`ls`が存在する場合は、シェルは`/usr/bin/ls`を実行します。先に書いてあるパスが優先して使われるわけです。

### 「パスが通らない」とは
つまり、「パスが通らない」とは、シェルに実行ファイルまでのパスを渡せてないということです。もっと詳しくいうと、`$PATH`に実行ファイルが存在するディレクトリのパスが書かれていないということになります。

なので「パスを通す」には、`$PATH`に実行ファイルが存在するのパスを追記してあげればいいわけです。追記するのは先頭と後ろのどちらでもいいとは思いますが、万が一同じ名前の実行ファイルがあった場合のために先頭に追記してあげることが多いです。
```bash
$ 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にしている場合は、以下の設定ファイルが順番に読み込まれます。全て実態はシェルスクリプトなので、単なる設定だけでなく、処理なども記述することができます。

1. `/etc/profile`
2. `~/.bash_profile`
3. `~/.bash_login`
4. `~/.profile`
5. `~/.bashrc`
6. `~/.bash_logout`

#### `/etc/profile`
全ユーザーに適用されるデフォルトの設定ファイルです。ユーザー単位では編集する必要はないので、ほとんどいじることはないと思います。ログイン時に全ユーザーに適用したい設定や処理がある場合に使います。

#### `~/.bash_profile`
あればログイン時に読み込まれます。無くても問題ありません。ユーザー単位の設定を記述します。ログイン時に一度だけ読み込めばいい設定や処理のみを記述し、余計なものは極力書かない方がいいようです。

環境変数はプロセス間で受け継がれるので、ログイン時のみ読み込めればOKなため、環境変数を記述するのによく使われます。なので、「パスを通す」ためには、このファイルに`export PATH=/foo/bar:$PATH`を追記することで、恒久的にパスを通すことができるようになります。

#### `~/.bash_login`
`~/.bash_profile`が存在しない場合にのみ、あればログイン時に読み込まれます。無くても問題ありません。

#### `~/.profile`
`~/.bash_profile`と`~/.bash_login`の両方が存在しない場合にのみ、あればログイン時に読み込まれます。無くても問題ありません。

特定のシェルに依存しない設定ファイルなので、bashに依存しない環境変数や、GUIで使う設定を書くのに使われます。

https://superuser.com/questions/789448/choosing-between-bashrc-profile-bash-profile-etc

#### `~/.bashrc`
あれば対話モードのbashを起動するときに読み込まれます。無くても問題ありません。

環境変数でない変数や、エイリアス、シェル関数、コマンドライン補完の設定、シェルオプション、プロンプト設定、`EDITOR`変数などのbashに依存する対話モード向けの設定や処理を書くのに使います。

逆に言うと、対話モードのbashを使用するときにのみ適用されるので、環境変数をここに書くのは適切ではありません（GUIや対話モードのbash以外から起動するプログラムなどに環境変数が渡らなくなるため）。

また、rsyncなどのsshパイプで問題が生じることがあるため、`~/.bashrc`に、標準出力や標準エラーに何かが出力されるような処理を書いてはいけないらしいです。

https://techracho.bpsinc.jp/hachi8833/2021_07_08/66396

#### `~/.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`の３つのうちで存在するファイル全てに対して環境変数を記述することです（３つ全て存在しない場合は`~/.profile`に記述してください）。

:::note info
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.

[pyenv - GitHub](https://github.com/pyenv/pyenv)
:::

例えば、`~/.bash_login`が存在しない場合は、`~/.bashrc`、`~/.profile`、`~/.bash_profile`に環境変数を記述します。
```bash
$ echo "export PATH=/foo/bar:$PATH" >> ~/.bashrc
$ echo "export PATH=/foo/bar:$PATH" >> ~/.bash_profile
$ echo "export PATH=/foo/bar:$PATH" >> ~/.profile
```
ちなみにパスは絶対パスで指定する必要があります。
### zsh
途中

### sh
途中

## 設定ファイルの変更を適用させる
設定ファイルを変更したら、以下のコマンドを実行しないと変更が反映されません。
    
```bash
$ source <設定ファイルのパス>
```
または、以下のコマンドでも反映できます。複数のファイルを編集した場合は、上記のコマンドだとファイル数分実行しないといけないので、下記のコマンドの方が楽です。
```bash
$ exec $SHELL
```

# その他参考文献
https://qiita.com/soarflat/items/09be6ab9cd91d366bf71

https://qiita.com/soarflat/items/d5015bec37f8a8254380

https://qiita.com/hirokishirai/items/5a529c8395c4b336bf31

https://qiita.com/magicant/items/d3bb7ea1192e63fba850

https://qiita.com/kkam0907/items/d21a775ce250d52b9d0e

https://qiita.com/shyamahira/items/260862743e4c9794b5d2