こういった場面で役立つのが、名前付きパイプ(FIFO)です。通常のパイプ(|)は2つのコマンドを直結するだけですが、名前付きパイプはファイルシステム上にパイプを「実体化」します。異なるシェルや無関係なプロセス同士でも、ファイル名を通じてデータをやりとりできる点が大きな違いです。
この記事では、
mkfifoコマンドの使い方を基礎から解説します。名前付きパイプの仕組みと通常のパイプとの違い、実際の作成手順、プロセス間通信への応用例、そしてよくある落とし穴まで、実機での動作確認をまじえて説明します。この記事のポイント
・mkfifo コマンドでFIFO特殊ファイル(名前付きパイプ)を作成できる
・名前付きパイプは異なるシェル・プロセス間でデータを受け渡す手段になる
・FIFOへの書き込みは読み手がいるまでブロックされる特性がある
・rm で削除するまでファイルシステム上に残り続ける点が通常パイプと異なる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
名前付きパイプ(FIFO)とは何か
Linuxには2種類のパイプがあります。・無名パイプ(anonymous pipe):コマンドラインで
| を使って2つのコマンドを直結するもの・名前付きパイプ(named pipe / FIFO):ファイルシステム上に特殊ファイルとして存在し、名前を通じて複数のプロセスが読み書きできるもの
FIFOは「First In, First Out」の略で、先に書き込まれたデータが先に読み出される順序を意味します。
ls -l で確認すると、ファイル種別が p(pipe)と表示されます。1. 通常パイプとの違い
通常のパイプ(|)は、左のコマンドの標準出力を右のコマンドの標準入力に直結します。生存期間はコマンドが動いている間だけです。# 通常パイプ: cmd1 と cmd2 が同じコマンドラインに存在しないと使えない ls /var/log | grep "\.log$" | head -5
p属性の特殊ファイル)mkfifo コマンド2. FIFOのブロッキング特性
名前付きパイプには、理解しておくべき重要な特性があります。書き手がFIFOに書き込もうとすると、読み手が現れるまでブロック(一時停止)されます。逆に、読み手がFIFOを開こうとしても、書き手が現れるまでブロックされます。これはカーネルレベルの仕様で、FIFOがデータを永続化しない(一時的なデータ転送路にすぎない)ことに由来します。この動作を前提にスクリプトを組まないと、プロセスがハング(応答なし)する原因になります。
mkfifoコマンドの基本的な使い方
1. 構文とオプション
# 基本構文 mkfifo [オプション] FIFO名 # 主なオプション # -m モード : 作成するFIFOのパーミッションを指定(例: -m 644)
2. FIFOを作成して確認する
# /tmp 以下に名前付きパイプを作成 $ mkfifo /tmp/mypipe # ls -l でファイル種別を確認(先頭が "p" になる) $ ls -l /tmp/mypipe prw-r--r--. 1 ec2-user ec2-user 0 6月 20 10:00 /tmp/mypipe # file コマンドで種別を確認 $ file /tmp/mypipe /tmp/mypipe: fifo (named pipe)
ls -l の先頭文字が p になっていれば、名前付きパイプとして正しく作成されています。サイズは常に 0 と表示されます。これはデータを蓄積するファイルではなくデータ転送路だからです。3. パーミッションを指定して作成する
# 所有者のみ読み書き可能な FIFO を作成 $ mkfifo -m 600 /tmp/private_pipe $ ls -l /tmp/private_pipe prw-------. 1 ec2-user ec2-user 0 6月 20 10:02 /tmp/private_pipe
-m 600 や -m 660 で制限します。名前付きパイプを使ったプロセス間通信の実践例
1. 基本動作の確認(2つのターミナルで試す)
まず、最もシンプルな動作確認から始めます。2つのターミナルを開いて試してください。ターミナルA(読み手):
# FIFOを作成して、読み出し待ちにする $ mkfifo /tmp/mypipe $ cat /tmp/mypipe # ← ここでブロックされる(書き手が来るまで待つ)
# 別のターミナルから FIFO に書き込む $ echo "hello from terminalB" > /tmp/mypipe
cat が受け取って表示します。# ターミナルAの出力 hello from terminalB $
2. シェルスクリプトでのバックグラウンド活用
実際のスクリプトでは、読み手をバックグラウンドで起動してからFIFOに書き込む流れが一般的です。#!/bin/bash FIFO=/tmp/work_pipe mkfifo "$FIFO" # 読み手をバックグラウンドで起動 while IFS= read -r line; do echo "受信: $line" done < "$FIFO" & READER_PID=$! # 書き手として複数行を送る for i in 1 2 3; do echo "メッセージ $i" > "$FIFO" done wait "$READER_PID" rm -f "$FIFO" echo "完了"
# 実行結果 受信: メッセージ 1 受信: メッセージ 2 受信: メッセージ 3 完了
&)で起動してからFIFOに書き込む」順序です。逆にすると書き手がブロックされてデッドロック状態になります。3. ログをリアルタイムで複数プロセスに配信する
tee と組み合わせると、1つのデータストリームを複数の処理に分岐できます。# 2つの FIFO を作成 mkfifo /tmp/pipe_a /tmp/pipe_b # 処理A: エラー行だけ抽出して記録 grep "ERROR" < /tmp/pipe_a > /var/log/myapp_errors.log & # 処理B: 行数をカウント wc -l < /tmp/pipe_b > /tmp/line_count.txt & # アプリのログを両方に送る tail -f /var/log/myapp.log | tee /tmp/pipe_a /tmp/pipe_b > /dev/null # 後片付け rm -f /tmp/pipe_a /tmp/pipe_b
tee は標準入力を複数の出力先に同時に流せます。tee ファイルA ファイルB のように書くと、FIFOを「中継ステーション」として複数のフィルタ処理を並列に動かせます。4. 進捗通知に使う(長時間バッチとの連携)
長時間かかるバッチ処理の進捗を、別のシェルから監視する用途にも使えます。#!/bin/bash PROGRESS_PIPE=/tmp/progress mkfifo "$PROGRESS_PIPE" # 監視プロセス(進捗表示)をバックグラウンドで起動 while IFS= read -r msg; do echo "[$(date +%H:%M:%S)] $msg" done < "$PROGRESS_PIPE" & MONITOR_PID=$! # 実際のバッチ処理 for step in "DB接続確認" "データ取得中" "集計処理中" "レポート出力中"; do sleep 1 # 処理の代わり echo "$step" > "$PROGRESS_PIPE" done echo "完了" > "$PROGRESS_PIPE" wait "$MONITOR_PID" rm -f "$PROGRESS_PIPE"
# 実行結果 [10:05:01] DB接続確認 [10:05:02] データ取得中 [10:05:03] 集計処理中 [10:05:04] レポート出力中 [10:05:05] 完了
トラブルシュート・よくあるエラー
1. プロセスがハング(ブロックされたまま止まる)
症状:FIFOに書き込もうとしたら、コマンドが返ってこない。原因:読み手プロセスが存在しないか、FIFOを開いていない。
# 読み手なしで書こうとするとブロックされる $ echo "test" > /tmp/mypipe # ← ここで止まる # 別ターミナルで確認 $ ps aux | grep "echo test" ec2-user 1234 0.0 0.0 ... T pts/1 0:00 bash -c echo test > /tmp/mypipe # 解除: 別ターミナルで読み手を起動するか、Ctrl+C で中断
・必ず「読み手を先に起動」してから書き手を動かす順序を守る
・
O_NONBLOCK フラグで開く(Cプログラムの場合)か、バックグラウンドに逃がす2. FIFOへの書き込みが「Broken pipe」で失敗する
症状:echo: write error: Broken pipe と表示される。原因:読み手が先に終了して、FIFOの読み込み側が閉じられた状態で書き込もうとした。
# 対策: 書き手と読み手の終了タイミングを合わせる # 読み手がループで待ち続けるよう設計するか # 書き手が終了したら読み手も終了するよう制御する
3. 既に同名のFIFOが存在してエラーになる
$ mkfifo /tmp/mypipe mkfifo: '/tmp/mypipe' を作成できません: ファイルが存在します # 解決策: 事前に削除または mktemp で一意な名前を使う $ rm -f /tmp/mypipe && mkfifo /tmp/mypipe # または mktemp で一時FIFOを安全に作成 $ TMPFIFO=$(mktemp -u) # -u は実際に作成しない(ファイル名取得のみ) $ mkfifo "$TMPFIFO"
4. スクリプト終了時にFIFOが残る
スクリプトが異常終了するとFIFOが削除されずに残ります。trap を使って確実に後片付けしましょう。#!/bin/bash FIFO=/tmp/cleanup_test mkfifo "$FIFO" # スクリプト終了時(正常・異常問わず)にFIFOを削除 trap "rm -f '$FIFO'" EXIT # ここでエラーが起きても FIFO は削除される some_command_that_may_fail
trap ... EXIT を使うことで、Ctrl+C や予期しないエラー終了でもFIFOが確実に消えます。本記事のまとめ
| やりたいこと | コマンド |
|---|---|
| 名前付きパイプを作成する | mkfifo /tmp/mypipe |
| パーミッションを指定して作成する | mkfifo -m 600 /tmp/mypipe |
| FIFOの種別を確認する | ls -l /tmp/mypipe(先頭が p) |
| FIFOを削除する | rm -f /tmp/mypipe |
| 1つのストリームを複数処理に分岐する | tee /tmp/pipe_a /tmp/pipe_b > /dev/null |
| 終了時にFIFOを自動削除する | trap "rm -f '$FIFO'" EXIT |
名前付きパイプは、異なるシェルやプロセス間でデータを受け渡す場面で真価を発揮します。通常のパイプでは実現できない「書き手と読み手の分離」ができるため、複雑なデータフロー設計やバッチ処理の進捗監視に活用できます。
注意点はブロッキング動作です。書き手と読み手の起動順序を意識し、
trap による後片付けをセットにする習慣をつけておくと、予期せぬハングやFIFOの残留を防げます。シェルスクリプトでプロセス間通信が必要になった時の選択肢として、ぜひ手元で動かして試してみてください。
Linux無料マニュアルを受け取る >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます