第24回 | シェルの「内部コマンド」と「外部コマンド」| なぜ UNIX は“部品の国”なのか

当サイトでは、コンテンツの一部に広告を掲載しています。
シェルの「内部コマンド」と「外部コマンド」| なぜ UNIX は“部品の国”なのか | UNIX Cafe

やさしい 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 のお作法です。

内部コマンド|シェルの「自家製メニュー」

では、それぞれの正体を深掘りしていきましょう。

内部コマンドとは?

内部コマンドは、
シェルの中に最初から組み込まれている命令です。

代表例

  • cd
  • export
  • alias
  • exit
  • jobsfgbg
  • source.

何が特別なのか?

内部コマンドは、シェル自身の状態を直接書き換えます

  • 今どこにいるか(カレントディレクトリ)
  • どんな環境変数を持つか
  • どんな設定を覚えているか

これらは、
別プロセスに任せることができません。

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

などに置かれた、
独立した実行ファイルです。

代表例

  • ls
  • cat
  • grep
  • sed
  • awk
  • find

実行の流れ

外部コマンドは、必ずこの流れで動きます。

  1. fork:分身を作る
  2. exec:別のプログラムに変身
  3. 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, aliasls, grep, git
実行速度速い(すぐ動く)普通(forkが必要)
シェルの状態変えられる変えられない

まとめ|内部と外部に分かれている理由

  • 内部コマンド → シェル自身を変えるためにある
  • 外部コマンド → 小さな部品として、自由につなぐためにある

この分業があるから、

  • UNIX は壊れにくく
  • やり直しができて
  • 50年以上生き残ってきました

UNIXの世界は、ただのコマンド操作ではありません。

それは、

  • 人が考え
  • 機械が実行し
  • 失敗してもやり直せる世界

なのです。

また次の一杯で、
この「部品の国」を、もう少し奥まで歩いてみましょう ☕️

さらに学びたいあなたへ

📘 用途ごとに選ぶ Linux のおすすめ本

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

のいのアバター のい UNIX Cafe 編集部

UNIX Cafe は、むずかしい言葉をできるだけ使わず、物語を読むような気持ちで気軽に学べる場所です。
プログラミングは、アイデアをコンピューターに伝えるための「ことば」。
簡単な単語と文法を覚えることで、誰でもターミナルから便利なコマンドを使えるようになります。
コーヒーを片手に立ち寄るような気持ちで、やさしいプログラミングの世界を、
そっとのぞいてみてください。

目次