【初学者向け】内部コマンドと外部コマンドの違い ～ LinuxとWindowsの話を混ぜて解説するな！

## はじめに

Linux では内部コマンドは**シェルビルトインコマンド**に相当します。bash などのシェルに組み込まれ個別の実行ファイルは存在しません。略してシェルビルトインやビルトインコマンド、日本語で組み込みコマンドなどと呼ばれることもありますが Linux では内部コマンドという用語はあまり使いません。**内部コマンドとは主に Windows の世界で使われる用語**で、コマンドプロンプトを起動した時に実行される`cmd.exe` （Windows コマンド プロセッサ、コマンドラインインタプリタ）に組み込まれているコマンドのことです。例えばコマンドプロンプトから、存在しないコマンドを実行すると以下のように「内部コマンド」という用語が出力されます。

```cmd
C:\Users\koichi>aaa
'aaa' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
```

シェルビルトインコマンドまたは内部コマンドに対する外部コマンドは Linux でも Windows でも使う用語ですが、**外部コマンドとは違い内部コマンドはLinux の世界では全く使わないとまでは言い切れないものの、一般的に使われている用語ではありません**。内部コマンドと言った場合は、おそらく Windows またはその前身の MS-DOS の話をしていると思われますが、もし「Linux の内部コマンド」の話をしているとしたら、それは本当に Linux の話をしているのか、Windows (MS-DOS) の話と混同していないのか注意する必要があります。なお macOS や UNIX でも Linux と同様に内部コマンドではなくシェルビルトインコマンドという用語が使われます。

この記事は**内部コマンドについてのおかしな記事や誤解を招く記事が多い**ため、それを正すために Windows の内部コマンドと Linux・macOS・Unix のシェルビルトインコマンドを一つの記事で説明しますが、この二つは用途は似ていてもさまざまな違いがあり混乱するだけなので、内部コマンドとシェルビルトインコマンドを混ぜて説明すべきではありません。**混ぜて説明しているものは大概内容がおかしい**です。

:::note warn
この記事のWindows の「内部コマンド」というのはコマンドプロンプトを起動しコマンドラインインタプリタ（`cmd.exe`）を使用する場合を前提としています。WSL や Git for Windows を使用している場合にはあてはまりません。WSL や Git for Windows は Windows の中に Linux や Linux 風の環境を作り出す仕組みとなっており、一般的な使い方では `cmd.exe` の代わりに bash などの Linux / Unix シェルを使用します。

bash を使用しているときは bash のシェルビルトインコマンド（と外部コマンド）を実行することができますが、`cmd.exe` の内部コマンドを直接実行することはできません。逆もまた然りで、`cmd.exe` またはシェルを実行しているときだけ、それぞれの内部コマンドまたはシェルビルトインコマンドを呼び出すことができます。
:::

## Windows の内部コマンドと外部コマンドの間違った説明

まず Windows の内部コマンドに関する間違い指摘しておきます。

1. **内部コマンドは標準でインストールされているコマンドのことではありません**。
    - 標準でインストールされている外部コマンドもあります。
    - OS に内蔵されているから内部コマンドと呼ぶのではありません。
    - コマンドラインインタプリタ（`cmd.exe`）に内蔵されているから内部コマンドです。
2. **内部コマンドはOS起動時にメモリに読み込まれるコマンドではありません**。
    - 今はコマンドプロンプトを起動したときにメモリに読み込まれます。
    - OS起動後にコマンドラインインタプリタ上にいた MS-DOS 時代ははるか昔です。
3. **外部コマンドは「OSに組み込まれていないコマンド」というわけではありません**。
    - どこまでを OS と考えるかによりますが、一般的には Windows 全体が OS です。
    - `MORE.COM` コマンドは OS (Windows) に組み込まれた外部コマンドと言えます。
    - 外部コマンドはコマンドラインインタプリタに組み込まれていないコマンドです。

コマンドラインインタプリタ（Linux ではシェル）は OS を構成するソフトウェアの一部と考えられますが、OS そのものではないことに注意してください。OS を構成するソフトウェアには OS 標準の外部コマンドも含まれます。内部コマンドかどうかは OS に含まれているかどうかではなく、コマンドラインインタプリタに含まれているかどうかです。

余談ですが `MORE.COM` って今でも拡張子 `.COM` だったんですね（互換性を考えれば当然か）。ファイルの最初が `MZ` で始まってるので恐らく `.EXE` と違いはないように思えますが、昔（MS-DOS 時代から初期の Windows？）は `.COM` は、プログラムサイズが 64 KB 以下（16 ビットコンピュータの1セグメントに収まるサイズ）で、実行ファイルのヘッダ（つまり `MZ` から始まるファイルの冒頭部分）がなく、いきなりプログラムが始まるものでした。より多くのプログラムサイズとデータメモリを扱えるように、管理情報をヘッダに埋め込んだものが `.EXE` です。

## Windows (MS-DOS) の内部コマンドの特徴

Windows のコマンドラインインタプリタには `DIR`、`MKDIR`、`COPY`、`DEL`、`REN`、`RMDIR` コマンドなどのファイル管理関連や `DATE`、`TIME` コマンドなど数多くの基本的なコマンドが内部コマンドとして組込まれています。このようなコマンドは Linux (Unix) のシェルではシェルビルトインコマンドとはなっておらず設計方針に大きな違いがあります。Windows のコマンドラインインタプリタの内部コマンドを見ると、最低限のファイル管理が行えるようになっていることに気が付きます。その理由を公式に説明している所はおそらく見つからないのではないかと思いますが、当時のパソコン事情から推測できます。

まず Windows のコマンドラインインタプリタである `cmd.exe` の前身は、MS-DOS の `COMMAND.COM` です。名前は違いますが本質的な機能は同じで、`cmd.exe` は `COMMAND.COM` の設計を引き継いだ後継プログラムと考えることができます。さて MS-DOS 時代ですが、当時は高価なハードディスクは追加で購入する外部記憶装置でした。MS-DOS がリリースされたのは 1981 年ですが、1985 年時点でハードディスクは 10 MB で 25 万円とか、1990 年時点の大容量 40 MB で 10 万円とかそのような価格だったようです。そのため読み書きが遅いフロッピーディスクドライブのみが搭載された状態のパソコンも使われていました。ハードディスクを持っていない場合、OS (MS-DOS) はフロッピーディスクに入れてフロッピーディスクから起動していました。そのような状況でファイル管理をするたびにディスクからコマンドを読み込んでいたら遅すぎてイライラしていたことでしょう。でもこちらはそこまで重要な問題ではありません。当時は遅くて当たり前で、遅ければ待つだけのことです。

初期のパソコンの多くはフロッピーディスクドライブを二台搭載しており、同時に二枚のフロッピーディスクをパソコンに差し込むことができました。しかし OS が入っているシステムディスクから起動し、例えばワープロソフトが入っているアプリディスク、かな漢字入力変換システム FEP（今で言う IME）の辞書データ、そして文書ファイルを書き込むデータディスクを考えると四台は欲しくなります。フロッピーディスクドライブを増設することもできましたが多くは二台しかないので、起動可能な最小限のシステムとアプリを一つのディスクにまとめたり、ディスクを入れ替えながら使うことになります。またデータファイルをあるディスクから別のディスクにコピーする場合、フロッピーディスクドライブは二台とも埋まってしまいます。こんな状況の中で基本的なコマンドを実行するたびに毎回ディスクから読み込む仕組みだと、何度もフロッピーディスクを入れ替えるという作業が発生してしまいます。待つだけならともかく何度もディスクをガチャガチャ入れ替えるのは面倒です。制限のきついパソコンを実用的に使うためには最低限の基本的なファイル管理コマンドぐらいはメモリに読み込んでいなければならないということです。

MS-DOS 時代はパソコンを起動したらコマンドラインインタプリタ (`COMMAND.COM`) が起動していたため「内部コマンドはOS起動時にメモリに読み込まれる」は当時としては正しいです。しかし Windows 時代ではコマンドプロンプトを実行して初めて内部コマンドはメモリに読み込まれ、コマンドプロンプトを終了するとメモリから削除されるという使われ方が一般的になりました。今では内部コマンドは OS 起動してもメモリに読み込まれたりしません。

ちなみに厳密な話をすると `COMMAND.COM` はすべてがずっとメモリに読み込まれているわけではなく、常駐部分と非常駐部分に分かれており、非常駐部分は大きなプログラムを実行したときに破棄されます（スワップアウトのことではありません）。これは当時の少ないメモリでどうにかやりくりしようとした結果です。そして `COMMAND.COM` に制御が戻った時に非常駐部分が破棄されていた場合は再度 `COMMAND.COM` がディスクから読み込まれます。もしディスクを入れ替えたりしていて `COMMAND.COM` が見つからない場合は、以下のようにディスクをドライブに入れるように要求されました。

```txt
COMMAND.COMのバージョンが違います.
COMMAND.COMの入っているディスクをカレントドライブに挿入してください.
どれかキーを押してください.
```

ところで、現在は Windows ではコマンドプロンプトに代わり PowerShell が使われるようになりましたが、PowerShell で内部コマンドに相当するものは何にあたるのでしょうか？ あまり使わないのでよく知らないのですが、標準コマンドレットとか、組み込みコマンドレットでしょうか？ 内部コマンドとは言わないような気がします。


## Linux (Unix) のシェルビルトインコマンドの特徴

シェルは OS （またはディストリビューション）標準のものだけではなく、追加でインストールすることもあります。Linux のシェルビルトインコマンドとは、その名の通りシェルにビルトイン（組み込み）されているコマンドのことです。シェルによって何がシェルビルトインコマンドであるかは異なります。**シェルビルトインコマンドはシェルに組み込まれているので `/bin` や `/usr/bin` 以下に実行可能なファイルはありません**。Windows の内部コマンドとは違いファイル管理などのOSの基本的な操作に必要なコマンドであってもシェルには基本的に組み込まれていません。具体的には、**cp、mkdir、ls コマンドなどはシェルビルトインコマンドではなく外部コマンドです**。もっともこれらのコマンドを組み込んだシェルを作ってはいけない決まりはないので、そういうシェルを作ろうと思えば作れますが、まずありません。

シェルビルトインコマンドはシェルに組み込まれているため、**シェルから実行する場合の「起動速度」はシェルビルトインコマンドの方が外部コマンドよりも速いですが「処理速度」はシェルビルトインコマンドのほうが速いわけでは有りません**。どちらが処理速度が速いかは実装によりますが一般的には作り込まれている可能性が高い外部コマンドのほうが速いでしょう。ただし起動速度は外部コマンドのほうが遅いため注意が必要な場面もあります。

| シェルビルトインコマンド | 外部コマンド |
| - | - |
| シェルに内蔵されたコマンド | シェルに内蔵されていないコマンドすべて　|
| コマンドの数は少ない | OS に標準インストールされている外部コマンドは多数ある　|
| 起動速度が速い | 起動速度が遅い（処理速度が遅いという意味ではない）　|
| 実体はどこにもない | `/bin` や `/usr/bin` などあらゆる場所にある |
| `cd` など | `cp`、`mkdir`、`ls` など<br>（Windowsの`COPY`、`MKDIR`、`DIR`などは内部コマンド） |

Linux (Unix) では**システムに必要な基本コマンドのほとんどは外部コマンド**として実装されています。Linux のシェルは元をたどれば、Unix のシェル（Thompson シェルや Bourne シェル）にたどり着きますが、Unix シェルはなるべくシェルに機能を組み込まないという設計で作られています。シェルにコマンドを組み込まないという設計はシェルを小さくシンプルに保つためのものだと考えられます。Unix ではどうしてもシェルで実装しなければいけないコマンド（`cd` コマンドなど）以外は外部コマンドとして実装されていますが、主に起動速度のパフォーマンスが理由でシェルに組み込んだほうがいいとシェル開発者が判断したコマンドはシェルに組み込まれました。他のシェルで組み込まれているとは限らないため、あるシェルに組み込まれたとしても外部コマンド版はそのまま残っていることに注意してください。**外部コマンドと同じ名前のコマンドが後からシェルに組み込まれる**というのが歴史的な流れです。ちなみに bash は `help` コマンドでシェルビルトインコマンドの一覧を出力することができます。例えば `enable` コマンドは bash のシェルビルトインコマンドであることがわかります。`enable` コマンドは指定したシェルビルトインコマンドを有効無効にするための（Linux ではなく）bash の機能です。

Windows (MS-DOS) では `COPY`、`MKDIR`、`DIR` コマンドは内部コマンドですが、Linux ではこれらに相当する機能を持つ `cp`、`mkdir`、`ls` コマンドは外部コマンドです。MS-DOS のコマンドラインインタプリタと Unix のシェルとで組み込むコマンドの方針に違いがある理由は、Unix は（開発当初は別として）マルチユーザーの OS であり、複数の人が同時に高価なコンピュータを共有して使うために設計されているからです。Unix の誕生は 1969 年、世の中に広く配布されたのが 1975 年、MS-DOS のリリースは 1981 年です。シングルユーザーの MS-DOS よりも古い Unix がマルチユーザーに対応していることに驚くかもしれませんが、そもそも Unix は性能が高いが高価なコンピュータ（ミニコン）用の OS で、コンピュータが高価すぎて一人がコンピュータを専有して対話的に使うなど考えられなかった時代のものです。人がコンピュータの前で考え込んだりすれば貴重なコンピュータリソースが待ち時間として無駄に消費されてしまいます。コンピュータを無駄なく動かし続ける重要な時代でした。待ち時間を減らすために複数人でコンピュータを共有しているので、パソコンのように個人の都合でフロッピーディスクを勝手に入れ替えるわけにはいきません。その代わり、高価なだけあってハードディスクの存在を前提とすることができ、必要なときにハードディスクからコマンドをメモリに読み込むことができました。個人用のパソコンが普及したのはその後で、パーソナル（個人）なコンピュータなだけあって性能が低い代わりに安価なコンピュータを一人で専有できるため、その特性で実用的に使えるように設計しているわけで、MS-DOS と Unix とで異なる設計になるのは必然なわけです。

**シェルビルトインコマンドと外部コマンドは意識して使い分けるようなものでは有りません**。シェルによって実装されているか、そうでないかの違いがあるというだけで同じように使うことができるように作られています。ただし実装の違いで実現可能な処理に違いがあったり、シェルビルトインコマンドのほうが起動速度が速いという特性の違いや、シェルビルトインコマンドと外部コマンドで動作が違うことがあるので、どちらが使われているのか把握することは重要です。コマンドがシェルビルトインコマンドなのか外部コマンドなのかを調べるには `type` コマンドを使うと良いでしょう。とはいっても繰り返しになりますが Linux (Unix) ではシステムに必要な基本コマンドのほとんどは外部コマンドなので、調べた結果はほとんどは外部コマンドです。

```sh
$ type echo
echo is a shell builtin

$ type cp # 外部コマンドの場合は絶対パスで出力される
cp is /usr/bin/cp

$ type ls # エイリアスの場合はこのように出力される
ls is aliased to `ls --color=auto'
```

補足ですが**外部コマンドは特別な理由がない限り絶対パスで書いてはいけません**。外部コマンドのパスは環境変数 `PATH` から探すものです。外部コマンドのパスは環境によって異なるので絶対パスで書いてしまうと他の環境で動かなくなる可能性があります。環境変数 `PATH` はシステムが設定した標準のパスをそのまま使用することが多いですが、基本的な考え方としてはコマンドを見つけられるようにユーザーが適切に設定するものです。ちなみにですが `which` コマンドは外部コマンドを探すとは限りません。zsh では `which` コマンドがシェルビルトインコマンドであり、シェルビルトインコマンドも探すことができてしまいます。

```sh:zshで実行した場合
# zsh ではシェルビルトインコマンドを見つけることができる
% which echo
echo: shell built-in command

# -a（見つかったものをすべて出力）でシェルビルトインコマンドと外部コマンドの両方を出力する
% which -a echo
echo: shell built-in command
/bin/echo

# envコマンド経由で外部コマンド版のwhichコマンドを使えば外部コマンドのパスを取得できる
% env which echo
/bin/echo
```

## さいごに

多くの記事は、Linux や macOS や Unix の用語として内部コマンドという用語を使っておらず、内容も一応は正しいと思います。しかし内部コマンドという用語を使っている場合は内容が怪しい記事が多いです（Windows の話であるとはっきり分かる場合は別です）。初学者の人が間違った用語を使ってしまうのは当たり前のことですが、そのときに内部コマンドという用語で検索すると、怪しい内容の記事が上位にヒットします。なぜなら正しい記事は内部コマンドという用語を使わないのでヒットしないからです。間違った用語から間違った結果を検索してしまっているわけです。Linux の話として内部コマンドという用語を検索した人は、今すぐ引き返してシェルビルトインコマンドで検索し直してください。

困ったことに「[Linux教科書 LPICレベル1 Version5.0対応](https://www.amazon.co.jp/dp/4798160490)」でも内部コマンドと言う用語が使われています。左側の「8点すべてのイメージを見る」でちょうど該当の P112 が表示されます。この本の内容は比較的まともそうに見えるのですがレビューは必要でしょうかね？ シェルビルトインコマンドのことを内部コマンドという用語で検索してもろくな記事にヒットせず、時代遅れか間違った内容に混乱させられます。Windows または MS-DOS の内部コマンドの説明をそのまま Linux でも同じだと思い込んで記事が書かれ、そしてそれを参照して間違った内容の記事が生まれるという悪循環が発生しているようです。この記事が上位にヒットし悪循環が断ち切られることを祈ります。