シェルについてまとめる

# はじめに
最近部屋の整理をしていたらUNIXの本を見つけシェルについていろいろと書かれていました。そういえばなんとなく使っていたけどシェルって何かと聞かれると分かりやすく説明できないなーと思い、今回の記事を書こうと思うに至りました。備忘録も兼ね、「ターミナルで初めてCUIに触れているけどシェルって何か分からない」「記事を見ながら環境設定していたらパス通せって言われたけど意味が分からない」というような人にも分かるように説明していこうと思います。

# カーネルとシェル
シェルについて触れる前にカーネルについて触れる必要があります。私たちが触っているパソコンはCPUやメモリなど様々な部品で出来ており、それらを**ハードウェア**といいます。そのハードウェアを制御するために**OS（オペレーティングシステム）** が使用されています。PCであればWindows,macOS,Unix,Linux、スマホであればios,Androidなどが有名です。そのOSの核となる部分を抽象的に**カーネル**と呼びます。あくまで抽象的なものなので「カーネルってＯＳの核となる部分なんだー」くらいのあいまいな理解で問題ありません。そしてそのカーネルとユーザのやりとりを仲介しているのが**シェル**です。

# シェルとは？
状況によって意味合いが大きく変わるので厳密な定義は難しいですが、とても簡単に定義すると以下のようになると思います。

**定義：カーネルとユーザのやりとりを仲介するプログラムのこと**

もう少し難しく言うとシェルは**コマンドインタプリタ**です。コマンドインタプリタとは利用者が打ち込むコマンドを解釈してそれに応じた動作をするプログラムです。広義で言えばシェルと言ったらＯＳを指定しないのですが、一般的にシェルというとUnixやLinux系OS出身のシェルを指すことが多いと思います。Windowsは基本的にGUIなのでCUIを全く使わない人もいますし、やはりUnix系などのシェルの方が印象が強い感じがします。とはいえWindowsにもカーネルもシェルももちろんありますので、誤解はしないようにしてください。

ここからはUnix系列のシェルについてもう少し深く掘り下げていきたいと思います。

# Unix系列のシェルの歴史と種類
先ほどまでの章で概要を説明しましたが、抽象的で結局シェルの実体はなんなの？という人が多いと思います。一番分かりやすく言うと**bash**や**zsh**がシェルです。「これがシェルだったのか！」となっている人が多いかもしれないですが、もう少しシェルの歴史にお付き合いください。（どうでもいいですがzshってゼッシュ、ズィッシュ、ゼットシェルみなさんなんて読んでますか）

いまも使われているOSの中で一番古いOSはおそらくUnixであり、そのUnixの初期バージョンで使われていたのが**Bourn Shell**です。**Bシェル**、実行コマンドがshであることから単に**sh**と呼ばれたりします。ここからUnix系列のシェルが次々と開発され、**C Shell(csh)**,**TC Shell(tcsh)**,**Bourne-Again shell(bash)**,**Z Shell(zsh)**,**Friendly interactive shell(fish)** などが開発されていきました。ここで今まで紹介したシェルを一度まとめましょう。


|シェル名|コマンド名|備考|
|:-|:-:|:-:|
| Bourn Shell(Bシェル)|sh|bashなどの元となったシェル|
| C Shell |csh|Bシェルの後発、Cシェル系と言われる派閥の大元|
| TC Shell|tcsh|Cシェルの上位互換|
| Bourne-Again shell |bash|Bシェルのオープンソース版として開発|
| Z Shell|zsh|多機能なシェル|
| Friendly interactive shell|fish|多機能かつ従来のものより分かりやすい|

2022年8月31日時点では**bash,zsh,fishの３つを知っておけば問題ない**と思います。なぜかというとほとんどのUnix,Linux系OSではbashを使っており、macOS Catalina 10.15以降のmacOSではzshがデフォルトシェルとなっていることからbashとzshを押さえておけば実用上はほとんど問題ありません。ただ最近は「fishはいいぞー！」という輩がqiita,zenn,僕の周りでもちらほら確認されているので「fishという分かりやすいシェルがあるんだー」くらいには頭にとどめておきましょう。ここからはbash,zshを前提として話を進めていきます。

# 具体的にシェルってどうやって実行するの？
bash,zshがシェルの代表格でそれを使えれば何とかなるってのは分かったけどどうやって実行するの？と思ったそこのあなた。実はシェルはログインしただけで実行されています。これは**ログインシェル**が起動するためです。ログインシェルとはその名の通りログインしたときに実行されるシェルで、ログインする度に知らず知らずにシェルを起動していたのです。正確には他の条件でもログインシェルが起動することがあるのですが、とりあえずログインするだけでログインシェルが実行されているということを頭に入れておきましょう。

ここで「ログインシェルは何を実行しているのだろう」という疑問が湧きます。シェルはカーネルとユーザの仲立ちをしているわけですから、コマンドを標準入力（キーボード）かファイルから読み込む必要があります。皆さんは無意識のうちにログインシェルを起動しているのですから当然標準入力からコマンドを打ち込んでいる訳はありません。ログインシェルはどこかのファイルを読み込んで実行しているはずです。ここからはログインシェルはどこのファイルを読み込んでいるのかを解説していきます。

ここからの流れは各Unix、Linuxディストリビューション（Linuxの種類みたいなやつ）などによって細かく変わりますが例としてbashの動作を例として解説します（参考[[1]]）。

#### ログインからログアウトまでの流れ

1. /etc/profile が存在する場合 /etc/profile を読み込んで実行
1. ~/.bash_profile が存在する場合 ~/.bash_profile を読み込んで実行
1. ~/.bash_login が存在する場合 ~/.bash_login を読み込んで実行
1. ~/.profile が存在する場合 ~/.profile を読み込んで実行
1. ログアウトする際に~/.bash_logout が存在する場合 ~/.bash_logout を読み込んで実行

これだけ見るとよく分からないので1つずつ解説していきます。まず最初の /etc/profile はすべてのユーザーに影響を与えるグローバルな初期設定が記述されており、システムプロファイルと呼ばれています[[2]]。つまり複数ユーザに影響を与えるこのファイルが個人ユーザの設定ファイルよりも優先して読み込まれるということです。基本的にこの記事の読者は複数ユーザーの管理者ではないと思うので、この部分は気にしなくても問題ありません。

次の3行分ですが、この **~/.bash_profile, ~/.bash_login, ~/.profile** の３ファイルはどれか1つのファイルしか読み込まれず、その優先順位は記述した順序で優先的に読み取られます[[1]]。その後ログアウト時にもファイルを読み込んで終了します。つまり、**この３つのファイルのいずれかに書いてある処理がログイン時にログインシェルによって実行されている**わけです。一般的にログインシェルの内容を書きたい場合は一番優先度の高い **~/.bash_profile**に書くことが多いです。

しかし、ログイン時以外にも実行したいシェルはどこに書けばいいのでしょうか。まず、ログインの後に改めてbashが起動された時の動作を解説していきます。

#### ログイン後に改めてbashが起動された時の動作

1. /etc/bash.bashrc が存在する場合 /etc/bash.bashrc を読み込んで実行
1. ~/.bashrc が存在する場合 ~/.bashrc を読み込んで実行

1行目は先ほどと同じようにユーザ全体の設定ファイルなので割愛します。大切なのは2行目です。**ユーザ個人の設定は ~/.bashrc に記述すればbash起動時に読み込まれる**ということです。これでログイン時に実行したい処理は ~/.bash_profile に、bash起動時に必要な処理や定義は ~/.bashrc に記述すれば問題ありません。たまにどちらのファイルに書くべきかという議論を見かけますが、これらの特徴を分かっていれば大きく間違えることはありません（処理速度とかを考え出すとまた別だと思いますが）。ここまでbashで紹介してきましたが、zshでもほとんど変わらない（はず）です。ファイル名がbashからzshに変わっているだけで、やること自体は変わりません。

ここまでの記述でどのファイルに書けばいいのか分からないというような人は疑問が解決したかと思います。次に実際に.bashrc と .bash_profileを見てみましょう。ここでは筆者のwslのUbuntu20.04LTSの.bash_profileを見せます。

```shell:.bash_profile
# ~/.profile: executed by the command interpreter for login shells.
# This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login
# exists.
# see /usr/share/doc/bash/examples/startup-files for examples.
# the files are located in the bash-doc package.

# the default umask is set in /etc/profile; for setting the umask
# for ssh logins, install and configure the libpam-umask package.
#umask 022

# if running bash
if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
        . "$HOME/.bashrc"
    fi
fi

# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then
    PATH="$HOME/bin:$PATH"
fi

# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/.local/bin" ] ; then
    PATH="$HOME/.local/bin:$PATH"
fi
```

長々と書いてありますが、

```shell
 if [ -f "$HOME/.bashrc" ]; then
        . "$HOME/.bashrc"
 fi
```

と書いてあるのが分かるでしょうか。これは ~/.bashrc を実行するということです。つまり、ログイン時にログインシェルは~/.bashrc を実行しています。たぶんデフォルトからいじっていないと思うのでデフォルトでこうなっているはずです。ですから困ったらとりあえず ~/.bashrcに書いておけばとりあえず実行はされます。

~/.bashrc の方は人によってかなり違うのですが環境変数とエイリアスの設定が出来ればだいぶ楽になると思います。

```shell:.bashrc
#yarnのパスを通している
export PATH=$PATH:'/home/username/.yarn/bin'

alias ll='ls -alF'
alias la='ls -A'
```

上のコードを理解する前に環境変数を理解しましょう。簡単に言うと**環境変数とはシェルやコマンドにまたがって共有される変数**です（いい説明が思いつかなかったのでかなりざっくり言ってます）。環境変数とよく比較されるものとして**シェル変数**がありますがこれは現在実行中のシェルだけで有効です。それに対して環境変数は新たなシェルや複数コマンドの間でも共有されます。代表的な環境変数として**PATH**が存在します。この変数にコマンドが存在するパスが設定されているおかげで、私たちは毎回パスを省略して書くことが出来ているわけです。つまり、**PATHを通すということは環境変数PATHにパスを省略して使用したいコマンドのパスを追加するということです。**

環境変数PATHはパスを「：」で区切っています。確認のためにターミナルで下記のコマンドを実行してみましょう。

```shell
echo $PATH
```
「：」区切られたパスが大量に出てきたと思います。この末尾に使いたいコマンドのパスを追加すればいいわけです。また環境変数にするためには**export** を付けなければならないため、パスを通したい場合は以下の記述を .bashrcもしくは.bash_profileに記述すればいいわけです。

```shell
export PATH=$PATH:'追加したいコマンドのパス'
```

環境変数の設定なのでたぶん.bash_profileに記述してもいいというか、そっちの方が毎回実行されないのでそっちの方がいい気がしますが、僕はめんどくさいので全部.bashrcに書いてます。ちゃんと分けて書かないとやばいよ！とかあればコメントください。

エイリアスについてはコマンドを省略して実行できるというだけです。

```shell
alias ll='ls -alF'
alias la='ls -A'
```

左辺のエイリアスの中身が右辺になるという簡単な仕組みですが、結構便利なので使いましょう。

どのファイルが実行されるか、どのように書けばいいか簡単に解説してきましたが、ファイルを書き換えたら再度読み込む必要があります。そういう場合は

```shell
source ファイル名
or
. ファイル名
```
と入力することで変更したファイルを読み込むことが出来ます。これをしないと変更が反映されないと焦ることがあるので注意しましょう。macはデフォルトで入っていたと思いますが、UbuntuなどではSourceコマンドを手動でインストールしたような気がします（気のせいかも）。僕は割と頻繁に変更するのでこのコマンド自体をエイリアスとして保存して実行しています。

# おわりに
シェルって何というところから設定ファイル、環境変数、パスの通し方など基本的なところは押さえたと思うので、この記事を読んだ皆さんは環境設定くらいでは困らないと思います。ただ細かいシェルスクリプトについては力尽きて書ききれなかったので気になる人は調べてみてください。役に立った、面白かったとおもったらLGTMしてくださると泣いて喜びます。

# 参考文献
[1]:https://blog.sgry.jp/entry/2019/11/09/232927
[2]:https://www.itmedia.co.jp/enterprise/articles/0803/10/news012.html
[3]:https://atmarkit.itmedia.co.jp/aig/03linux/shell.html
[4]:https://recruit.cct-inc.co.jp/tecblog/os/shell/
[5]:https://shiimanblog.com/engineering/shell/
[6]:https://ja.wikipedia.org/wiki/Unix%E3%82%B7%E3%82%A7%E3%83%AB
[7]:http://www.cc.kyoto-su.ac.jp/~hirai/text/shell.html
[8]:https://ramble.impl.co.jp/1503/
[9]:https://e-words.jp/w/%E3%82%B7%E3%82%A7%E3%83%AB.html
[10]:https://tooljp.com/windows/chigai/html/Linux/loginShell-interactiveShell-chigai.html
[11]:https://blog1.mammb.com/entry/2019/12/01/090000
[12]:https://www.itmedia.co.jp/enterprise/articles/0803/10/news012.html
[13]:https://blog.sgry.jp/entry/2019/11/09/232927
[14]:https://qiita.com/kure/items/f76d8242b97280a247a1
[15]:https://qiita.com/fuwamaki/items/3d8af42cf7abee760a81
[16]:https://zenn.dev/b1essk/articles/0785ea4f88d9ae
[17]:https://codezine.jp/unixdic/w/%E3%82%B7%E3%82%A7%E3%83%AB%E5%A4%89%E6%95%B0%E3%81%A8%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0


[[1]]:bashの.profileや.bashrc等を実行する動作仕様
[[2]]:bashが起動時に実行するファイル
[[3]]:シェル
[[4]]:【10分でわかるかもしれない】シェル（shell）とは？
[[5]]:【mac】シェルをカスタマイズしてみた |【bash】【zsh】【fish】
[[6]]:Unixシェル
[[7]]:シェルとは
[[8]]:OS、カーネル、シェルってなんやねんって話
[[9]]:シェル 【shell】
[[10]]:ログインシェルとインタラクティブシェルの違い
[[11]]:bash の初期化ファイル .profile, .bashrc, .bash_profile の使い分けと管理方針
[[12]]:bashが起動時に実行するファイル
[[13]]:bashの.profileや.bashrc等を実行する動作仕様
[[14]]:シェル変数と環境変数の違いをコマンドラインで確認する
[[15]]:Pathを通すとは、環境変数とは
[[16]]:【PATHを通す】を理解する
[[17]]:シェル変数と環境変数