ターミナルやシェルについてのモヤモヤを解消した！【学びログ】

# いい加減、ターミナルとシェルをちゃんと理解しておきたい

PC新調する予定なので、それを機に、改めてターミナルやシェルの設定をきちんとしたいと考えていました。しかし、よく考えてみると、私はこれまでターミナルやシェルを十分に理解せずに使ってきました。理解していないものを適切に設定することは難しいですよね。

そこで、ターミナルとシェルについて抱いていた疑問やモヤモヤを解消することにしました。気の向くままに調べたことをログ感覚で残したので、体系的な説明をした記事ではありませんが、同じようにターミナルやシェルをなんとなく理解して使っていた人にとっても、参考になればと思います。

:::note info
本記事の対象はmacユーザーを想定しています。
:::


# まずはターミナルとシェルの違いについて、ちゃんと調べて自分なりのイメージを固めました

ターミナルとシェルは違うものという認識はあったのですが、曖昧な認識だったので、下記の記事を参考にターミナルとシェルの違いを明確にしました。

https://zenn.dev/k_log24/articles/582af625164d41

上記の記事の説明が簡潔で分かりやすいので、本記事では改めてターミナルとシェルが何者かを説明することはしませんが、私自身は、ターミナルとシェルの関係性をブラウザとWEBアプリの関係性に対比させると理解しやすいかなと思いました。


ブラウザが様々なWebアプリとのインターフェースになっているように、ターミナルは異なるシェルとのインターフェースとして機能します。

例えば、ブラウザの設定でデザインを変更することでタブのデザインを変えられるように、ターミナルの設定でターミナル自体の色を変更することができます。一方で、Webアプリの設定を変更することでUIを変更できるように、シェルの設定を変更することで表示内容を変更することができます。

また、ブラウザにはChromeやSafariなど様々な種類があり、ユーザーは好みに合わせて選択できます。同様に、ターミナルアプリにも複数の種類があり、macに標準搭載されているターミナルアプリ以外にも、他のターミナルアプリを使用することができます。

例えば最近はWarpという高性能なターミナルアプリがホットらしいです。（ターミナルとシェルの違いがわかっていなかった私にとっては、「ターミナルアプリを切り替える...とは？」という状況でした）

https://www.warp.dev/

要するに、ターミナルはシェルを操作するための入り口であり、シェルは実際にコマンドを実行する環境だと言えます。ブラウザとWebアプリの関係と同じように、ターミナルとシェルは密接に関係しながらも、それぞれ独立した役割を持っているのです。

# 「ユーザーのログインシェル」と「ターミナルのデフォルトシェル」という2つの異なる概念があると知った

これまで私は便利だという噂だけ聞いて、fishという多機能でいい感じのシェルを使っていました。しかし、前述の通り、シェルの仕組みは深く理解していない状態でした。そこで、改めてシェルを基本から理解するために、macでデフォルトで採用されているzshに戻すことにしました。そして、なるべく自分の手で設定を行っていこうと考え、使用するシェルを変更しようとして、問題にぶち当たりました。

「うむ、シェルが変更できん」

ターミナルで使用するシェルを切り替える方法を調べると、大体このような説明に遭遇します。

https://qiita.com/to3izo/items/2b3ca2c67f69ca2acfdd

その通り、実行しましたが、切り替わりません。そこで、ターミナルの環境設定を確認すると、「あれ、なんか使用するシェルの指定方法が２つある」となりました。

1. デフォルトのログインシェル
1. コマンド（完全パス）

![スクリーンショット 2024-03-23 18.01.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166480/07d0cdb1-cbf1-9fd1-88a6-f96f7fc1dbab.png)

ここで、私の勘違いしていたのは、 **「ターミナル起動時に使用されるシェル」** と **「`echo $SHELL`で表示されるシェルや`chsh -s xxxx`で指定するシェル」** を同じものだと思っていたことです。

```
echo $SHELL
```

で現在使用しているシェルを表示するという説明がありますが、これは正確には現在使用している**ログインシェル**を表示しています。環境変数`$SHELL`に保持されているのはこのログインシェルと呼ばれるものなのです。

https://e-words.jp/w/%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3%E3%82%B7%E3%82%A7%E3%83%AB.html#:~:text=%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3%E3%82%B7%E3%82%A7%E3%83%AB%EF%BC%88login%20shell%EF%BC%89%E3%81%A8,%E3%81%AE%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3%E3%82%B7%E3%82%A7%E3%83%AB%E3%81%A8%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E3%80%82

ログインシェルの概念については、私自身の理解も曖昧な部分があるため、詳細には立ち入りませんが、ここで押さえておきたいのは

- ログインシェルは、OSにログインしているユーザーごとに設定されるであり、ユーザーがシステムにログインする際に起動されるシェルであること

- ターミナルで使用するシェルは、ターミナルアプリケーションごとに設定されるシェルであり、その名の通り、ターミナルを起動した際に使用されるシェルであること

という違いです。両者は別々に設定され、必ずしも同じシェルが使用されるとは限りません。

```
chsh -s /bin/zsh
```

上記のコマンドは、ログインシェルを変更するためのものであり、ターミナル起動時に使用されるシェルを直接指定しているわけではありません。

ターミナルで使用するシェルを切り替えるには、ターミナルの設定でログインシェルを使用するように指定する必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166480/ddb9959f-8c11-3e40-fb7b-70a9237fc3f8.png)

これまで、ターミナルで使用するシェルをログインシェルではなく、直接パスで指定していたため、ログインシェルを変更してもターミナルで使用するシェルが切り替わらなかったのです。

つまり、ログインシェルとターミナルで使用するシェルは別々に設定されており、ターミナルの設定でログインシェルを使用するように指定することで、両者を連動させることができるのです。

# 改めてターミナルに入力しているコマンドってなんだろうって思った

IT業界に入りたての研修時に「ターミナルでは、Linuxコマンドを入力してCUI操作ができるんだよ」と教わり、慣れない`cd`や`ls`をせっせこ入力して、なにこの黒い画面怖いと怯えていました。

そして、なんとなく刷り込まれたのが「ターミナルに入力するもの＝Linuxコマンド」というイメージでした。

いやでも、これは大嘘ですよね。`git`しかり`npm`しかり、普段から当たり前のように使用しているコマンドはLinuxコマンドではありません。

改めて、私がターミナルに入力しているコマンドってなんなんだろうと自問しても、答えられない自分がいたので、コマンドについて整理してみました。

### コマンドは3種類に分類される

まず、ターミナルに入力されたコマンドは、シェルにそのまま渡されるので、ここでいうコマンドとはシェルが受け付けてくれるコマンドということになります。そして、それは次の３種類に分類されます。

- シェルビルトインコマンド:
    - シェル自身に組み込まれているコマンド
    - シェルの内部で処理され、シェルの状態を変更したり、環境変数を操作したりする
    - 例: `cd`, `echo`, `alias`, `export`など
- シェルスクリプト:
    - シェルが理解できるシェルスクリプトというプログラミング言語で記述されたテキストファイル
- 実行可能ファイル（外部コマンド）:
    - 外部コマンドとも呼ばれ、実行可能な単体のプログラム。要するに、実体はコンパイル済みのバイナリファイル
    - 例: `ls`, `git`, `npm`, `python`

細かいことは置いておいて、シェルで使用するコマンドはこの3つのカテゴリーに集約されるという理解が重要です。

つまり、cdなどの一部のLinuxコマンドは、シェルに組み込まれた「シェルビルトインコマンド」なんです。シェル自体がそのコマンドを直接実行できるように設計されているから、ターミナルで入力できて当然ですよね。

「シェルスクリプト」については一旦放置しておきましょう。そうすると、残りのコマンドは全て「実行可能ファイル」を指定しているに過ぎないんです。よく考えてみれば当たり前のことなのですが、gitなどのアプリケーションをインストールする際には、「実行可能ファイル」がインストールされているんですね。そして、ターミナルでコマンドを入力する際には、その実行可能ファイルを指定しているだけなんです。

例えば、`git`コマンドを入力すると、実際には`git`という名前の実行可能ファイルが起動されているんですよ。これは、他のコマンドでも同じことが言えます。`npm`や`python`なども、それぞれ対応する実行可能ファイルが存在しているんです。

これまで、コマンドを入力すれば何かが起こるという感覚で使っていましたが、その裏では実行可能ファイルが動作しているんですよね。まあ考えれば当たり前のことでしたが、このことを意識すると、コマンドの動作原理がよりクリアになり、もやもやが晴れていく感じがしました。

# パスを通すってそういうことだったのか

ここで「パスを通す」という言葉について考えてみましょう。みなさんも、コマンドが実行できないときに、「パスを通せばいい」という解決策を見たことがあるのではないでしょうか。私は、そういった記事を見つけては、そのまま真似して、なんとなくパスを通してきました。

ここで先ほどの話を思い出しましょう。コマンドとは、実行可能ファイルを指定していることがわかりましたよね。つまり、gitコマンドを実行するときは、gitという実行可能ファイルを指定しているんです。「いやいやgitってどうみてもファイルパスがないじゃないですよね？」と疑問に思った方もいらっしゃるかもしれません。私はそうでした。でも、なぜファイルパスを指定しなくても、gitコマンドが実行できるのでしょうか？

実は、これこそが「パスを通す」ことの意味なんです。「パスを通す」とは、実行可能ファイルが存在するディレクトリを、シェルの環境変数であるPATHに追加することなんです。PATHには、コマンドを検索するディレクトリが登録されています。つまり、PATHに登録されているディレクトリ内にある実行可能ファイルは、ファイルパスを指定しなくても、コマンド名だけで実行できるようになるんです。

例えば、gitコマンドの実行可能ファイルが、`/usr/local/bin`ディレクトリにあるとします。このディレクトリがPATHに登録されていれば、gitコマンドを実行するときに、`/usr/local/bin/git`というファイルパスを指定しなくても、単に`git`と入力するだけで実行できるんです。

なので、新しいコマンドをインストールしたのに実行できないときは、そのコマンドの実行可能ファイルが存在するディレクトリが、PATHに登録されていない可能性が高いんです。そういった場合に、PATHにそのディレクトリを追加する、つまり「パスを通す」ことで、コマンドが実行できるようになるんですね。

「パスを通す」ことの意味が理解できると、コマンドの実行方法だけでなく、コマンドの検索方法についても深く理解することができます。シェルはPATHに登録されたディレクトリを順に検索し、最初に見つかった実行可能ファイルを実行するんです。

# パスを通すことの意味が理解できると、モヤモヤしていたことが色々解消されました。

macユーザーなら、Homebrewを使用して、アプリケーションをインストールする人も多いと思います。私がHomebrewをインストールしたときにも、「パスを通せ！」的なことを指示されました。

参考👇

https://zenn.dev/tet0h/articles/a92651d52bd82460aefb

もうこれの意味が分かりますね。Homebrew経由でインストールされたアプリの実行可能ファイルが格納されるディレクトリのパスを通せってことですよね。

他にも、使用しているシェルを変えると今まで使用できていたコマンドが使用できなくなるという問題です。これももうなぜか理解できました。`PATH`はシェルごとに設定されている環境変数であるため、変更後のシェルでは、ちゃんとパスが通ってなかったってことですね。

# おわりに

本記事では、シェルスクリプトやシェルの詳細な設定方法については触れませんでした。まだ私自身がそこまで深く調べられていないからです...でも、ターミナルとシェルについての基本的な概念や仕組みを理解することで、これまで抱えていたモヤモヤがすっきりと晴れました。

ターミナルとシェルの全体像が見えてきたことで、これからシェルスクリプトやシェルの設定について学ぶ際も、スムーズに理解を深められそうな予感がしています。きっと以前よりも効率的に、そして楽しみながら学べるはずです。

今回の学びメモが、あなたの学びのきっかけになれば嬉しいです。