
やさしい UNIX & Linux | 第24回
はじめに|同じ「コマンド」なのに、正体が違う?
ターミナルで入力するコマンドは、
どれも同じように見えます。
cd /tmp
lsでも実はこの2つ、
生まれも、役割も、動き方も違う存在です。
今回は、このシェルの「内部コマンド」と「外部コマンド」の違いについて考えてみます。
シェルには2種類のコマンドがある
シェルのコマンドは、大きく分けて次の2つです。
- 内部コマンド(組み込みコマンド)
- 外部コマンド
違いは、とてもシンプルです。
- シェル自身を動かすか
- シェルの外の道具を呼び出すか
見分け方は?「type」コマンドに聞いてみよう
目の前のコマンドが「内部コマンド」か「外部コマンド」か。それを見分けるための、とっておきの方法があります。それが type コマンドです。
type cd
type ls実行すると、あなたの環境(MacやLinuxなど)に合わせて、「type」がこんな風に正体を教えてくれます。
内部コマンドの場合(例:cd)
MacやUbuntuなどで実行すると cd is a shell builtin と表示されます。
これは「cdはシェルの組み込み(builtin)メニューですよ」という意味。
つまり、シェルと一体化している内部コマンドである証拠です。
外部コマンドの場合(例:ls)
通常は /bin/ls のように、その道具が置かれている「住所(パス)」が表示されます。
ただし、Macなどでは ls is an alias for ls -G -F と表示されることがあります。
これは「lsという名前に、色付けなどのオプションを付けた『alias』をつけています」という意味です。aliasがつけられている場合も、その正体は外にある道具、つまり外部コマンドです。
💡 ちょっと休憩:which ではなく type を使う理由
コマンドの場所を調べるときに、which を使う方も多いかもしれません。which は、シェルの外にある「実行ファイル(外部コマンド)」を探すための道具です。
そのため、cd のような内部コマンドに対して which cd と打っても、何も表示されなかったり、正体が分からなかったりします。一方 type は、内部コマンド・外部コマンド・エイリアスまで含めて、そのコマンドが何者なのかを教えてくれます。コマンドの正体を確かめたいときは、which ではなく type を使うのが UNIX のお作法です。
内部コマンド|シェルの「自家製メニュー」
では、それぞれの正体を深掘りしていきましょう。
内部コマンドとは?
内部コマンドは、
シェルの中に最初から組み込まれている命令です。
代表例
cdexportaliasexitjobs,fg,bgsource(.)
何が特別なのか?
内部コマンドは、シェル自身の状態を直接書き換えます。
- 今どこにいるか(カレントディレクトリ)
- どんな環境変数を持つか
- どんな設定を覚えているか
これらは、
別プロセスに任せることができません。
cd が外部コマンドにできない理由
もし cd が外部コマンドだったら、どうなるでしょう。
シェル
├─ fork → 子プロセス
│
├─ 子プロセス
│ └─ /tmp に移動
│
└─ 親のシェルは元のまま結果はこうなります。
$ pwd
/home/noi
$ cd /tmp
$ pwd
/home/noi # 変わっていない移動したのは 子プロセスだけ
外部コマンドは子プロセスとして実行されるため、親であるシェルの状態を直接変更することはできません。
そのため、カレントディレクトリを変える cd は、最初からシェルの内側に組み込まれた内部コマンドとして
用意されているのです。
cd が「自分で歩く命令」である理由
店長は、自分とそっくりな別の人を一人作り、「ちょっと郵便局まで行ってきて」と声をかけます。
その人は確かに郵便局へ向かい、目的地にたどり着きます。
けれど、そのあいだも店長は、同じ場所に立ったままです。
もし店長が「自分が立っている場所」を変えたいのなら、自分と似た誰かに任せることはできません。
必要なのは、店長自身が、一歩を踏み出すことなのです。
cd と source が“外部コマンドにできない”理由
source(または .)も、cd と同じ立場です。
source config.shこれは、
- ファイルを読む
- 今のシェルで
- 設定を書き換える
という命令です。
たとえば、設定ファイル(.zshrc など)を書き換えたあとに source を使うのは、変数やエイリアスなど「新しく覚えた設定を、今のシェルの中に刻み込むため」です。もしこれが外部コマンドだったら、設定を読み込んだ瞬間に子プロセスが終了し、今のシェルには何も残らないことになってしまいます。
外部コマンド|シェルの「お取り寄せ道具」
外部コマンドとは?
外部コマンドは、
/bin/usr/bin
などに置かれた、
独立した実行ファイルです。
代表例
lscatgrepsedawkfind
実行の流れ
外部コマンドは、必ずこの流れで動きます。
- fork:分身を作る
- exec:別のプログラムに変身
- wait:終わるのを待つ
これが、UNIX の基本リズムです。
fork が速い理由|Copy-on-Write
fork は「コピー」と言われますが、
実際には ほとんどコピーしません。
- 親と子は同じメモリを共有
- 書き換えた瞬間だけコピー
- 多くの場合、その前に exec される
これが Copy-on-Write です。
だから UNIX の世界では、「分身を作る=重い作業」という常識は当てはまりません。
wait しない世界で起きること
もし親が wait を呼ばなかったら?
- 子プロセスは終了している
- でも記録が回収されない
- プロセス表に残り続ける「抜け殻」
これが ゾンビプロセスです。
UNIX が wait を大切にするのは、
始めることと同じくらい、終わらせることが重要、だと考えているからです。
なぜ UNIX は「部品の国」なのか
UNIX は、
- 1つの道具は小さく
- 役割は単純に
- つなぎ方は自由に
という世界を選びました。
たとえば、次のコマンドです。
cat file.txt | grep error | wc -lここでは、
- 読む
- 探す
- 数える
という 小さな部品 が、パイプでつながっています。
れぞれの部品は fork によって分身し、パイプというバケツリレーで、
次から次へとデータを手渡していきます。
UNIX が生み出す力。それは、個々の道具の「大きさ」からではなく、
「どうつながっているか」から生まれるのです。
これが、50年以上愛され続ける UNIX の思想です。☕️
| 特徴 | 内部コマンド | 外部コマンド |
|---|---|---|
| 正体 | シェルの機能の一部 | 独立したファイル |
| 例 | cd, exit, alias | ls, grep, git |
| 実行速度 | 速い(すぐ動く) | 普通(forkが必要) |
| シェルの状態 | 変えられる | 変えられない |
まとめ|内部と外部に分かれている理由
- 内部コマンド → シェル自身を変えるためにある
- 外部コマンド → 小さな部品として、自由につなぐためにある
この分業があるから、
- UNIX は壊れにくく
- やり直しができて
- 50年以上生き残ってきました
UNIXの世界は、ただのコマンド操作ではありません。
それは、
- 人が考え
- 機械が実行し
- 失敗してもやり直せる世界
なのです。
また次の一杯で、
この「部品の国」を、もう少し奥まで歩いてみましょう ☕️
さらに学びたいあなたへ
📘 用途ごとに選ぶ Linux のおすすめ本

