概要
普段何気なくターミナルへ入力してリターンキーを押すコマンド群は、シェル内部でどのように解釈され、システム上で起動されているのでしょうか。本記事では、Bashがコマンドを探索して識別する順序から、プロセス生成(Fork & Exec)、ファイルディスクリプタを介した入出力制御、さらにはサブシェルでの環境分離まで、一連の実行ライフサイクルを実機のターミナル出力をもとに体系的に紐解きます。
1. コマンドの探索と種別判定(エイリアス・組込・PATH解決)
入力文字列を正しく実行するため、Bashはそれがシェル組み込み(ビルトイン)、エイリアス、定義済み関数、あるいは外部バイナリのいずれに該当するかを特定します。この識別状況を調査する最適な手段が type コマンドです。
例として cd を評価すると、ファイルシステム上のバイナリではなく、現在のシェル自身が直接処理する組み込み機能(shell builtin)として認識されていることが確認できます。
⚙️ [主要オプション]
-t : 対象の種別を表す単一キーワード(builtin、alias、file、keyword、function)のみを返します。-a : 同名で利用可能なすべての候補(エイリアス定義、ビルトイン、PATH 上の同名ファイル)を検索順に列挙します。-p : 対象が外部実行ファイルとして解決される場合のみ、その完全パスを表示します。

続けて type を用いて ls の属性を調べてみます。
ビルトインとして扱われる cd とは対照的に、ディスク上の /usr/bin/ls に配置された独立したバイナリファイルとして実行される実態が分かります。

外部バイナリの探索経路を突き止めるツールとして which コマンドも有用です。環境変数 PATH に設定された検索ディレクトリ群を走査し、呼び出される実体の配置場所を特定します。ls を指定すると、実際にロードされる絶対パス /usr/bin/ls が即座に出力されます。
⚙️ [主要オプション]
-a : 最初に見つかった候補で探索を終了せず、PATH 全体で見つかる該当バイナリを漏れなく一覧表示します。

外部コマンド検索の基準となるディレクトリルートは、echo $PATH で参照可能です。
出力されるコロン(:)区切りのリストをシェルが先頭(左側)から順番に走査し、最初に出現した実行権限付きファイルをターゲットとして確定させます。

2. プロセス生成と実行メカニズム(Fork & Execの挙動)
シェルが外部コマンドを立ち上げる際、親プロセスを複製して子プロセスを起動するツリー構造を可視化するため、まず pstree を内包する psmisc パッケージをセットアップします。

カレントシェルのプロセスIDを示す特殊変数 $$ を指定し、pstree -p $$ を実行します。
親プロセスである対話型 bash が fork システムコールによって自身を複製し、exec を経て pstree 子プロセスを起動している親子関係とその固有PIDが明瞭に描画されます。

次に、bash -c 'sleep 2' & を使って、新たなサブシェルプロセス経由で sleep 2 を非同期バックグラウンド(&)実行してみます。
親シェルは待機せず即座にプロンプトの操作権を取り戻し、背後では独立した子プロセスが指定時間だけ動作を継続します。

バックグラウンド処理の生存状態を追跡するため、ps -ef | grep [s]leep を発行します。
指定した待機時間(2秒)を経過した後はプロセスが正常に終了して回収されるため、マッチするプロセスは存在せず結果は空行となります。
![ps -ef | grep [s]leepの実行結果](/ubuntu/20261013_015729_57/captures/s02_c04_ps_-ef___grep__s_leep.png)
3. 標準入出力とパイプライン処理(I/Oリダイレクション)
ファイル記述子とデータストリームの接続関係を検証するため、mkdir -p /tmp/bash_demo && echo 'Hello Bash World' > /tmp/bash_demo/sample.txt を実行して検証用の作業フォルダとサンプルデータを準備します。

作成されたテキストの内容は cat /tmp/bash_demo/sample.txt で読み出し、標準出力(stdout)へと流し込みます。
格納されていた文字列 Hello Bash World が端末へ問題なく印字されるのを確認します。

LinuxにおけるI/Oの土台となるファイルディスクリプタ(FD)の割り当て状況は、ls -l /proc/$$/fd からリアルタイムに把握できます。
標準入力(0)、標準出力(1)、標準エラー出力(2)がそれぞれ擬似端末デバイスやパイプへ結びつけられ、抽象化されたストリームとして制御されている様子が読み取れます。

複数のコマンドを連鎖させるパイプラインでは、先行プロセスの出力ストリームが後続プロセスの入力ストリームへと直結されます。cat /tmp/bash_demo/sample.txt | tr 'a-z' 'A-Z' では、ファイルから出力された小文字ストリームがそのまま tr の標準入力へ渡され、大文字へと変換された HELLO BASH WORLD が最終出力されます。

4. 環境変数の継承とサブシェル(Subshell)のスコープ境界
シェル内の変数がどの範囲まで有効に機能するかは、スコープ定義とプロセスの境界によって決定されます。まず DEMO_LOCAL='LocalValue' で現在のセッションに限定されたローカルシェル変数を定義します。
この変数は現行シェル空間のメモリ内にのみ保持され、新しくフォークされた別プロセスへは伝播しません。

一方、外部プロセスや子プロセスにも値を引き渡したい場合は、export DEMO_GLOBAL='GlobalValue' を用いて環境変数テーブルへ明示的に登録します。export された変数は、親プロセスが生成するあらゆる子プロセス環境の初期化情報として自動的に複製・継承されます。

丸括弧で囲んだ (echo "[서브셸] Local: $DEMO_LOCAL, Global: $DEMO_GLOBAL") は、カレントセッションのメモリ状態をそっくり引き継ぐサブシェルをフォークしてコードを評価します。
親環境のクローンとして起動するため、export の有無に関わらずローカル変数・環境変数の双方がそのまま参照可能であることが分かります。
![(echo “[서브셸] Local: $DEMO_LOCAL, Global: $DEMO_GLOBAL”)の実行結果](/ubuntu/20261013_015729_57/captures/s04_c03_echo___%EC%84%9C%EB%B8%8C%EC%85%B8__Local___DEMO_LOCAL__Global___DEMO_GLO.png)
対して、まったく新しいBashプロセスを独立して立ち上げる bash -c 'echo "[자식셸] Local: $DEMO_LOCAL, Global: $DEMO_GLOBAL"' を実行すると挙動が変化します。
未エクスポートのローカル変数はプロセスの壁を越えられずブランクとなり、グローバル環境変数としてエクスポートされた値のみが正常に子プロセスへ受け渡される違いがはっきりと確認できます。
![bash -c ’echo “[자식셸] Local: $DEMO_LOCAL, Global: $DEMO_GLOBAL”‘の実行結果](/ubuntu/20261013_015729_57/captures/s04_c04_bash_-c__echo___%EC%9E%90%EC%8B%9D%EC%85%B8__Local___DEMO_LOCAL__Global.png)
ここまで、LinuxのBashシェルが入力されたコマンドを解釈し、プロセスとして具現化して入出力を束ねるまでのコアアーキテクチャを検証してきました。
シェルの内部動作メカニズムを正しく把握しておくことは、堅牢なシェルスクリプトの設計や高度な障害切り分けにおいて強力な基礎力となります。