FIG-085

パイプとファイルディスクリプタ — 縦棒1本が、何をつなぎ替えているのか

OS 2026.09.07 公開 読了 約11分

シェルで書く 縦棒(パイプ)は、左のコマンドの出力を右のコマンドの入力にするものだと説明されます。それは正しいのですが、実際に何がつなぎ替えられているのかまでは見えません。

答えはファイルディスクリプタという、プロセスが持つ番号つきの口です。縦棒1本が起こしているのは、この番号のつなぎ先を書き換えるという、ただそれだけの操作。図1を1ステップずつ進めて、その瞬間を見てください。

PIPE & FILE DESCRIPTOR — 縦棒がつなぎ替えているもの
$ cat access.log | grep ERROR
grepで拾う文字
cat access.log 読んで、そのまま出すだけ
fd 0 stdin 端末(キーボード)
fd 1 stdout 端末(画面)
fd 2 stderr 端末(画面)
PIPE
書き口 読み口
パイプはまだ無い
grep ERROR 読んで、合う行だけ出す
fd 0 stdin 端末(キーボード)
fd 1 stdout 端末(画面)
fd 2 stderr 端末(画面)
端末(画面)
STEP 0 / 4
縦棒を書かなければ、出力は端末へ向かう
どのプロセスも3つの口(fd 0・1・2)を持って生まれます。何もしなければ3つとも端末につながっていて、だから cat の出力は画面に出ます。「次へ」で縦棒を書き足してください。
図1 — 番号は変わらない。変わるのは「その番号の先に何があるか」だけ

ファイルディスクリプタ — プロセスが持つ番号つきの口

プロセスが外とやり取りするとき、相手がファイルでも端末でもネットワークでも、すべて「番号」で扱います。この番号がファイルディスクリプタ(fd)です。生まれた時点で3つ用意されていて、0 が stdin(入口)、1 が stdout(出口)、2 が stderr(エラー用の出口)と決まっています。

重要なのは、プログラム側は番号しか知らないということです。cat は「fd 1 に書く」としか書かれていません。その先が画面なのか、ファイルなのか、パイプなのかをcat 自身は気にしていない。だから外側から自由につなぎ替えられます。

縦棒がやっているのは、つなぎ替えだけ

シェルが縦棒を見つけると、まずカーネルにパイプを1本作らせます。パイプは書き口と読み口が対になった、名前のない一方通行の土管です。

そのうえでシェルは、左のプロセスの fd 1 を書き口へ、右のプロセスの fd 0 を読み口へつなぎ替えてから、それぞれのコマンドを起動します。図1のステップ2で、両側の行が同時に書き換わるのがその瞬間です。cat も grep も、自分がつなぎ替えられたことを知りません。

左が書いた内容は土管に溜まり、右が読んだぶんだけ減ります。溜まりすぎれば左は自動的に待たされ、右が読み終わるまで進めません。この待ち合わせのおかげで、巨大なログでも全部をメモリに載せずに流し込めます。

stderrはパイプに乗らない

縦棒がつなぎ替えるのは fd 1 だけです。fd 2(stderr)は端末につながったまま。図1の最後のステップで、cat のエラーメッセージだけがパイプを迂回して画面に直行するのはこのためです。

パイプでつないだのにエラーだけが画面に出てくるのは、この仕様どおりの動きです。エラーも一緒に流したいときは 2>&1 と書きます。これは「fd 2 のつなぎ先を、fd 1 と同じところにしろ」という指示で、結局やっていることは図1と同じつなぎ替えです。

用語ミニ辞書
ファイルディスクリプタ
プロセスが入出力先を指すのに使う番号。0/1/2は最初から決まっている。
stdin / stdout / stderr
それぞれ fd 0 / 1 / 2。入口・出口・エラー用の出口。
パイプ
書き口と読み口が対になった一方通行の通り道。縦棒を書くと作られる。
つなぎ替え(dup2)
ある番号の指す先を別のものに差し替える操作。リダイレクトの正体。
2>&1
fd 2 のつなぎ先を fd 1 と同じにする指定。エラーもパイプに乗る。

まとめ

縦棒は魔法ではなく、「土管を1本作り、両側のfdをそこに向け直す」という手続きです。番号は最後まで 0・1・2 のまま変わりません。変わるのはその番号の先に何がつながっているかだけ。ここが腑に落ちると、リダイレクトも 2>&1 も、同じ1つの操作の言い換えだと分かります。