「PATHを通す」とどうなるのか【プログラミング初心者】

環境構築をすることが多くなり、PATHを通す機会が増えました。
今までは書いてある通りのコードを意味も理解せず追加してPATHを通していましたが、そのコードの意味をちゃんと理解して、PATHを通すとは何をしているのかを知りたいと思い調べました。

**この記事のゴール**
:triangular_flag_on_post: PATHを通すと**どうなるのか**理解する。
:triangular_flag_on_post: 通したPATHを**読める**ようになる。
（今回はnodenvを使用する際に通すPATH `export PATH="$HOME/.nodenv/bin:$PATH"`の意味を理解したいと思います）
# 1.PATHを通すとどうなるのか
## そもそもパスとは何か
「PATHを通す」を理解する前に、そもそもパスとは何かを調べます。

パス(path)とは…
>コンピュータ内で特定の資源の所在を表す文字列のことをパスという。
ストレージ（外部記憶装置）内でファイルやディレクトリ（フォルダ）の位置を表すのに用いられるが、他の用途でも使われる。
[パスとは - IT用語辞典 e-Words](http://e-words.jp/w/%E3%83%91%E3%82%B9.html)　より

特定のディレクトリの場所を示す、道順のようなものです。
ex：Desktopのパス
`/Users/mac（※ユーザ名）/Desktop`

:four_leaf_clover: メモ知識：pathは日本語で「経路」という意味を持つ

## 環境変数のPATH
「PATHを通すとどうなるか」を知るには、**環境変数**の1つである`PATH`の理解が重要になります。
環境変数とはなにか、というところから調べてみます。

## 環境変数とは
OSが提供する機能の1つです。
>環境変数とは、OSが設定値などを永続的に保存し、利用者や実行されるプログラムから設定・参照できるようにしたもの。プログラムの実行時などに必要となる、利用者やコンピュータごとに内容が異なる設定値などを記録するために用いられる。
[環境変数とは - IT用語辞典 e-Words](http://e-words.jp/w/%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0.html)　より

環境変数は以下のコマンドで確認できます。
`$ printenv`

出力結果

```bash:bash

mac$ printenv

TERM_PROGRAM=iTerm.app
SHELL=/bin/bash
TERM=xterm-256color
USER=mac
...省略
PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
...省略
HOME=/Users/mac
LOGNAME=mac
SECURITYSESSIONID=186a8
_=/usr/bin/printenv
```


`TERM_PROGRAM`、`SHELL`、`TERM`といったワードが全て**環境変数**と呼ばれるものです。

それぞれの変数に対して値が入っています。
TERM_PROGRAM=`iTerm.app`
SHELL=`/bin/bash`

環境変数一覧の中に**`PATH`**があることが確認できます。

```bash
PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
```

値は`/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin`ですが、`：`は区切りの意味なので、区切りで改行すると以下の値が入っていることが分かります。

```bash

/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

# 区切り：で改行
/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin
```

:four_leaf_clover: メモ知識：シェルやターミナル上で環境変数を使用するときは、**環境変数の前に`＄`**をつけることで使用できる。



```bash:bash
# 環境変数PATHの内容を表示する
$echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
```

## 環境変数PATHの役割
PATHは環境変数の1つであり、`/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin`といった値が入っていることが分かりました。（通しているPATHによって値は異なります）

では、この環境変数PATHに値を入れることで何が起こるのでしょうか？

PATHは、**コマンド実行ファイルが保存されているディレクトリの場所を設定する**変数で、PATHに登録されたディレクトリにあるコマンドは、**コマンド名のみ**で実行できるようになります。

:four_leaf_clover: メモ知識：コマンド … シェルなどが解釈して実行する、システムへの指示

ターミナルでコマンド実行ファイルのパスを入力すると、コマンドを実行します。
例えば、printenvコマンドは/usr/bin/printenvと入力することで実行できます。

```bash:bash
$which printenv
/usr/bin/printenv

# printenvコマンドは/usr/binの中にあるため/usr/bin/printenvで実行できる。
$/usr/bin/printenv
TERM_PROGRAM=iTerm.app
SHELL=/bin/bash
TERM=xterm-256color
・・・省略
```

しかし、`$printenv`のみでも同じ出力結果を得られます

```bash:bash

$printenv
TERM_PROGRAM=iTerm.app
SHELL=/bin/bash
TERM=xterm-256color
・・・省略
```

なぜ`/usr/bin/printenv`と書かずに`printenv`だけでも実行できるのでしょうか？
それは、**環境変数PATHに`/usr/bin`がある（＝パスが通っている）**からなんです。

```bash

PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

# 区切り：で改行
/usr/local/bin
/usr/bin
# ↑これ
/bin　　　　　
/usr/sbin
/sbin
```

シェルはコマンドが**パスの指定なし**(/usr/bin/printenvではなく`printenv`）で入力された時、**環境変数PATHに設定してあるディレクトリへコマンド実行ファイルを探しに行き、同じ名前のファイルがあればそれを実行**します。

:warning: 正確には、パスなしでコマンドを実行する際、シェルは`「エイリアス」→「ビルトインコマンド」→「環境変数PATHに登録されているディレクトリにある実行コマンド」`の順番で探し、最初に見つけたものを実行します。
・[PATHを理解して、コマンドの在りかを探してみよう](https://www.atmarkit.co.jp/ait/articles/1605/09/news013_2.html)

:bulb: `$printenv`を実行した時の流れ

```bash

# 1. printenvコマンドを実行
$printenv

# 2. エイリアス、ビルトインコマンドを確認。該当するコマンドがない場合は環境変数PATHを確認します。

# 3. 環境PATHを確認
PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

# 先頭に記述されているディレクトリから順番にそのディレクトリ下にprintenvの実行ファイルがあるかを確認していきます
/usr/local/bin
 ↓
/usr/bin
 ↓
/bin
 ↓　　　　　
/usr/sbin
 ↓
/sbin

# 4. /usr/local/binの下にprintenvの実行ファイルがあるか確認します。存在しない場合は、次に記述されているディレクトリを確認します
/usr/local/bin　# printenvの実行ファイルなし
 ↓
/usr/bin

# 5. /usr/binの下にprintenvの実行ファイルがあるか確認し、printenvコマンドが存在することを確認
/usr/binにprintenvの実行ファイルがある!

# 6. /usr/binの下にあるprintenv実行ファイルが実行されます
/usr/bin/printenvの実行

# 出力結果
TERM_PROGRAM=iTerm.app
SHELL=/bin/bash
TERM=xterm-256color
・・・省略
```
このような流れにより、**コマンド実行ファイルが入っているディレクトリのパス**を**環境変数PATHに追加**する（＝**PATHを通す**）ことで、**コマンド名だけで実行**できるようになるのです。

## まとめ
:triangular_flag_on_post: PATHを通すとどうなるのか 
**PATHを通したディレクトリ内にあるコマンド実行ファイル**を、**コマンド名のみで実行**できるようになる。逆に、PATHを通さないとコマンド名のみでは実行できない。

### 寄り道① PATHが通ってない時のエラー
PATHが通っていないのにコマンド名のみで実行すると、`command not found`といわれます。このエラーがでたときはPATHがちゃんと通っているか確認してみましょう。

 ```bash:bash
#nodenvコマンドがあるディレクトリのPATHを通さずに$nodenvを実行してみる

#ファイルパスを指定して実行
$/Users/mac/.nodenv/bin/nodenv -v
nodenv 1.3.1+11.5024679

#コマンド名のみで実行
$nodenv -v
-bash: nodenv: command not found
#PATHが通っていないためコマンドを見つけられない
```
### 寄り道② /usr/binにあるコマンド実行ファイルをみてみる
`/usr/bin`ディレクトリには、`printenv`コマンドの他にもたくさんのコマンド実行ファイルが入っています。
どんなコマンドが入っているのでしょうか？ターミナル上で確認してみようと思います。

```bash:bash
# /usr/binの中身を表示
$ls /usr/bin
2to3-@                              machine*
2to3-2.7@                           mail*
AssetCacheLocatorUtil*              mailq@
AssetCacheManagerUtil*              mailx*
AssetCacheTetheratorUtil*           make*
BuildStrings*                       makeinfo*
CpMac*                              malloc_history*
DeRez*                              man*
...省略

```
みたことのないコマンドがたくさんあります。/usr/binのPATHは通っているので、**ここにあるコマンドはコマンド名だけで実行することができます**。
/usr/binの他にも、今PATHが通っているディレクトリの中身を確認するのも面白いと思います。

# 2.通したPATHを読む
PATHを通すとどうなるか理解できたところで、実際にPATHを通すコードを読んでみたいと思います。
PATHの通し方については詳しく説明しないので、知りたい方は以下の記事をぜひ参考にしてみてください。
・[MacでPATHを通す](https://qiita.com/nbkn/items/01a11392921119fa0153)

今回はnodenvのPATHを通すコードを読んでいきます

```
export PATH="$HOME/.nodenv/bin:$PATH"
```

nodenvの環境構築は以下の記事を参考にして行いました。
・[nodenv を使って Mac に Node.js の環境を構築する](https://qiita.com/1000ch/items/41ea7caffe8c42c5211c)


では`export`から読んでいきます。

`export 〜`で環境変数を変更します。
:four_leaf_clover: メモ知識：export … 環境変数やシェル変数を設定するコマンド

`PATH=〜`で**PATH変数**に**値を代入**しています。
代入する値は`"$HOME/.nodenv/bin:$PATH"`です。

シェルは`$環境変数`を使用すると、**環境変数を展開**することができます。

```bash:bash
# 環境変数HOMEの値を確認
$echo $HOME
/Users/mac

# 環境変数PATHの値を確認
$echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

# bash_prifileに export PATH="$HOME/.nodenv/bin:$PATH" を追加

#.bash_profileの更新
$source ~/.bash_profile

# 環境変数PATHの確認
$echo $PATH
PATH=/Users/mac/.nodenv/bin/nodenv:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

＄HOME と $PATH が展開されている！！
```
詳しく内容を読み解いていきます。
`$HOME/.nodenv/bin:$PATH`の`$HOME`は**/Users/mac**、`＄PATH`は**/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin**になっています。

そのため、`$HOME/.nodenv/bin`は**/Users/mac/.nodenv/bin/nodenv**となります。

`：`で区切りを表し、そのあとに`$PATH`がきているので、**/Users/mac/.nodenv/bin/nodenv** + 区切り **:** + PATHの値**/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin**になります。

よって環境変数PATHの値には、**もとのPATH**に**/Users/mac/.nodenv/bin/nodenv**が追加されたものが代入されることになります。

```bash
# PATHの値
PATH=/Users/mac/.nodenv/bin/nodenv:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

# 区切り:で改行
/Users/mac/.nodenv/bin/nodenv　
#　↑追加されている
/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin

```

## まとめ
:triangular_flag_on_post: nodenvで通したPATHを読む 

```
export PATH="$HOME/.nodenv/bin:$PATH"
```
このコードは、**PATH変数の値の前**に`$HOME/.nodenv/bin:`を追加し、その値を**PATH変数に代入**する、という意味である。


### 寄り道① 代入する値を書く時の注意
:warning:　代入する値を`''(シングルクォーテーション)`で囲むと**変数の展開がされない**

```bash
#ダブルクォーテーション
# export PATH="$HOME/.nodenv/bin:$PATH"
$echo $PATH
PATH=/Users/mac/.nodenv/bin/nodenv:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

#なし
# export PATH=$HOME/.nodenv/bin:$PATH
$echo $PATH
PATH=/Users/mac/.nodenv/bin/nodenv:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

#シングルクォーテーション
# export PATH=’$HOME/.nodenv/bin:$PATH’
$echo $PATH
$HOME/.nodenv/bin:$PATH
#　↑ 環境変数の展開がされていない
```

### 寄り道② $PATH:$HOME/.nodenv/bin

```
export PATH="$PATH:$HOME/.nodenv/bin"
```
という書き方も見たことがありますが、これはPATHの値の**後ろ**に`:$HOME/.nodenv/bin`を追加し、その値をPATHに代入する、という意味になります。PATHを通すコードの読み方が理解できたので違いが分かりますね。

---
PATHを通す、ということについて自分なりに調べてみました。「とりあえずやっていたこと」が何をしていたのか理解できると、変更を加える時やエラーが起きた時に対応できるようになるなーと思いました。

#参考
- [読めばわかるMACでのPATH設定を完全理解](https://reffect.co.jp/windows/full_understanding_mac)
- [【初心者でもOK!】＄PATHを理解するためのMacの環境変数について](https://yuu.1000quu.com/understanding_the_env)
- [Linux入門~「パスを通す」とは~](https://qiita.com/Naggi-Goishi/items/2c49ea50602ea80bf015)
- [Pathを通すとは、環境変数とは](https://qiita.com/fuwamaki/items/3d8af42cf7abee760a81)
- [PATHを通すとは？（Mac OS X）](https://qiita.com/soarflat/items/09be6ab9cd91d366bf71)