組み込みコマンド

シェルの基本的な機能に関する説明は今回で最後だ。最後に取り上げるのは、シェルにあらかじめ含まれている「組み込みコマンド」である。通常のコマンドのように振る舞うコマンドだが、実態はシェル内部で実装された機能で、C言語の関数として実装されていることが多い。

まず、シェルで提供されている基本的な組み込みコマンドとその内容を紹介しておく。

組み込みコマンド 内容
: 0で終了するコマンド。trueコマンドと同じ挙動
. ファイル 指定されたファイルを読み込んで実行。ファイルがスラッシュを含んでいる場合にはパスとして処理され、そうでない場合には環境変数PATHに含まれているディレクトリ下のファイルとして、それ以外の場合にはカレントディレクトリに存在するファイルとして処理される
[ testコマンドと同じ
alias [名前[=値]] 「名前=値」の形式で指定された場合にはエイリアスの設定、名前のみ指定された場合にはその名前に紐付けられている値の表示、何も引数がなかった場合には設定されているエイリアスを一覧表示
bg [ジョブ] 指定されたジョブをバックグラウンドで処理継続。なにも指定されなかった場合には現在のジョブをバックグラウンドで処理継続
bind [キー [コマンド]] キーバインドを設定、または引数が指定されなかった場合には現在設定されているキーバンドを一覧表示
break [数字] フロー制御処理
builtin コマンド 指定した組み込みコマンドを実行。組み込みコマンドを同名の関数で上書きしている場合など、明示的に組み込みコマンドを実行するために使用できる
cd [ディレクトリ] 指定されたディレクトリをカレントディレクトリに設定。1つ前のディレクトリは-でアクセス可能。環境変数CDPATHが設定されている場合、リストされているディレクトリ以下を検索対象として利用する。引数が指定されなかった場合に環境変数HOMEで設定されているディレクトリをカレントディレクトリに設定
chdir cdの別名
command [ユーティリティ] 指定したユーティリティを実行。エイリアスや関数ではなく、実際のコマンドを実行しようとする
continue [数字] フロー制御処理
echo 文字列を表示。-nで改行コードの出力を抑える
eval 文字列 指定された文字列を空白区切りで結合し、再パースして実行する
exec [コマンド] シェルプロセスを指定されたコマンドプロセスに置き換える
exit [ステータス] シェルプロセスを終了する。ステータスが指定された場合、指定されたステータスをシェルの終了ステータスに設定する
export [名前=[値]] 指定された変数を環境変数に設定。変数名が指定されなかった場合、現在設定されている環境変数を一覧表示する。値も指定されている場合、変数に値を設定するとともに変数を環境変数に設定する
false 0以外で終了するコマンド。falseコマンドと同じ挙動
fc インタラクティブシェル状態で入力されたコマンドの一覧表示、編集、再実行などを実施
fg [ジョブ] 指定されたジョブをフォアグラウンドへ移行。ジョブが指定されなかった場合、現在のジョブをフォアグラウンドへ移動
getopts オプション文字列 変数 POSIX互換のgetoptsコマンド。オプション文字列は:が指定された場合にはオプションに値指定があることを意味する。引数のインデックスはOPTINDに、オプションの値はOPTARGに保持される。getopt(1)コマンドはすでに非推奨とされている
hash [コマンド] シェルが保持しているコマンドハッシュテーブルの中身の一覧表示および削除などの操作
jobid [ジョブ] 指定されたジョブにおけるプロセスIDを出力。ジョブが指定されなかった場合、現在のジョブに対して処理を実施
jobs [ジョブ] 指定されたジョブの情報を表示。ジョブが指定されなかった場合、すべてのジョブの情報を一覧表示。情報はジョブID、ステータス、コマンド名など
kill killコマンドと同じ
local [変数] 関数における変数のスコープを設定
printf printfコマンドと同じ
pwd カレントディレクトリのパスを表示
read 変数 … 標準入力から1行データを読み込み、空白分解した後、指定された変数へ代入する。指定された変数の1つ目に、分解したデータの1つ目を、指定された変数の2つ目に、分解したデータの2つ目を代入していく。指定された最後の変数に、分解された残りすべてのデータが代入される
readonly [名前=[値]] 指定した変数をリードオンリーに設定する。リードオンリーに設定された変数は値を上書きすることもunsetすることもできなくなる
return [終了ステータス] 指定された終了ステータスで関数を終了する
set 引数が指定されなかった場合、現在設定されている変数を一覧表示する。オプションが指定された場合、そのオプションの有効無効を切り替える。—が指定された場合、それに続くデータをシェルの引数として再設定する
setvar 変数 値 指定された値を指定された変数に代入する。通常、setvarではなく「変数=値」による代入のほうが良いとされる
shift [数字] 位置パラメータを指定した数字の分だけシフトする。数字が指定されなかった場合、1が指定されたのと同じ動きをする
test testコマンドと同じ
times シェルプロセスおよび子プロセスが使用した時間を表示
trap シグナル シグナルに対するトラップを設定
true 0で終了するコマンド。trueコマンドと同じ挙動
type [名前] 指定されたコマンドの種類と場所などを表示
ulimit [リミット] リソースリミットの表示および設定(getrlimit(2))
umask [マスク] マスクを設定(umask(2)、chmod(1))
unalias [名前] 指定されたエイリアスを削除
unset [名前] 指定された変数を削除
wait [ジョブ] ジョブの完了待ち

組み込みコマンドはその全てを覚えなければならないというものではない。必要になったときに、このようなコマンドがある、ということが出てくるようにざっと概要を把握できていればよいと思う。

シンプルさを求めるなら、組み込みコマンドなど用意せずに、全て外部のコマンドにすればよいと考えるかもしれない。しかし、組み込みコマンドには存在意義がある。その点に言及しておこう。

組み込みコマンドでないと処理できないパターン

まず、コマンドとして実行することができず、組み込みコマンドでなければ実現できない機能がある。最もわかりやすいのはcdだろう。カレントディレクトリの情報はシェルそのものが保持する必要がある。外部コマンドとしてcdを用意しても意味がないのである。

仮にcdを外部コマンドとして実装したとする。指定されたパスをカレントディレクトリとして設定するものの、その設定はcdプロセスが保有しているだけで、cdが終了してしまえばなくなってしまう情報だ。シェルそのものがカレントディレクトリの情報を保有していなければカレントディレクトリを変更する意味がなく、組み込みコマンドとして実装するしかないことになる。

ジョブ制御を行うfgとbg、エイリアスを管理するaliasとunalias、キーバインドを設定するbind、シェルプロセスを置き換えるexec、シェルを終了するexit、環境変数へ変数を変更するexport、変数のスコープや上書きを制御するlocalとreadonly、位置パラメータを制御するshift、トラップを設定するtrapなどなど、シェルの内部で実装しないと効果がないものがある。これが組み込みコマンドが存在している最大の理由だ。

実行速度を高速化したいパターン

もう1つ、外部のコマンドとして実装されていても動作としては問題ないのだが、処理速度を高速化したいために組み込みコマンドとして実装することがある。ハードウェア性能によっては組み込みコマンドで実装してもほとんど効果が出ないこともあるのだが、歴史的にもいくつかのコマンドは外部コマンドから組み込みコマンドとして取り込まれている。

最も代表的なのかtestや[だ。これらは外部のコマンドとして実装しておくよりも組み込みコマンドとして実装しておいたほうがパフォーマンス的に有利なことが多い。それ以外にもprintfやkillコマンドなどが組み込みコマンドとして取り込まれていることがある。

実装が簡単なので取り込まれているパターン

終了ステータスが0になるtrueや、終了ステータスが0以外になるfalseはシェルスクリプトでもよく使われているコマンドだ。これらのコマンドは実装がとても簡単なので、シェルに取り込むのも簡単だ。このため、外部コマンドではなく組み込みコマンドとして実装されていることも多い。

逆に意味のない外部コマンドもある

OSによっては、組み込みコマンドでなければ意味のないコマンドが外部コマンドとしてインストールされていることがある。動作的にこれは全く意味がない。POSIX互換を実現するために便宜上インストールされているだけで、実際には有効に機能はしない。そういうコマンドがあることも知っておくとよいかもしれない。

参考資料