mkfifoコマンドって使ってますか?

  • 147
    Like
  • 5
    Comment
More than 1 year has passed since last update.

Macを含むUNIXユーザーの皆さん

mkfifoコマンドって普段どのくらい使ってますか?

技術的にはとてもオモシロいんですけど、かく言う私も普段はベタなシェルスクリプトばかり書いているので使いどころがなかなか無いんですよねー。

mkfifoコマンド入門

mkfifo。聞き慣れない方のために、軽く解説しておきます。

まずは同じホストでターミナルを2つ開いておいてください。そして片方のターミナル(#1)で次のように打ち込んでおきます。これでhogepipeという名のちょっと不思議なファイルが出来ます。

ターミナル#1
[richmikan@myhost ~]$ mkfifo hogepipe

そしたら、ls -lコマンドで内容を確認してみましょう。行頭を見ると、"-"(通常ファイル)でもない、"d"(ディレクトリー)でもない、珍しいフラグが立っていますよ。

ターミナル#1
[richmikan@myhost ~]$ ls -l hogepipe
prw-rw-r-- 1 richmikan staff 0 May 15 00:00 hogepipe

"p"とは一体……

これは一体何物か?とりあえず中身を見てみましょうか。ということで、cat して中身を見てみましょう。

ターミナル#1
[richmikan@myhost ~]$ cat hogepipe

(あれ? 応答が返ってこない!)

そうなんです。まるで引数ナシでcatコマンドを実行したかのように(=どこにも繋がっていない標準入力を読もうとしているかのように)固まってしまいました。

でも[Ctrl]+[C]で止めるのはちょっと待ってください。ここで先程立ち上げておいたもう一つのターミナル(#2)から、今度はhogepipeに対して何かechoで書き込んであげてください。こんなふうに……

ターミナル#2
[richmikan@myhost ~]$ echo "Hello, mkfifo." > hogepipe
[richmikan@myhost ~]$ 

すると何も無かったかのように終わりましたが、ターミナル#1を見てみてください。

ターミナル#1
[richmikan@myhost ~]$ cat hogepipe
Hello, mkfifo.
[richmikan@myhost ~]$ 

いつの間にかcatが終了していて、先程ターミナル#2に打ち込んだ文字列が表示されています。

実はこれがmkfifoコマンドで作った不思議なファイル、「名前付きパイプ」の挙動です。

  1. 名前付きパイプから読み出そうとすると、誰かがその名前付きパイプに書き込むまで待たされる。

  2. 名前付きパイプへ書き込もうとすると、誰かがその名前付きパイプから読み出すまで待たされる。

という性質があるのです。今は1の例を行いましたが、ターミナル#2のechoをターミナル#1のcatより先に打ち込めば今度はechoがcatの読み出しを待ちますので試してみてください。

応用例

これがなかなか無いんですけど、例えばこういうのはどうでしょう。

  • 外部Webサーバー上に、定点カメラ映像を プログレッシブJPEG 画像ファイル(最初はぼんやり表示されて読み進めるほどクッキリ表示されるアレ)として配信するサーバーがある。
  • ただしそのWebサーバーは人気があって帯域制限が激しく、JPEG画像を最後まで ダウンロードするのには相当時間がかかる。
  • 上記のファイルがダウンロードでき次第、3人のユーザーのpublic_htmlディレクトリーにコピーして共有したい。でもできれば ボンヤリした画像の段階から見せられるように したい。

まぁこんな要求があったとして、次のようなシェルスクリプトを書けば解決してあげられるかな、と思うわけです。

画像を読み込んでくるシェルスクリプト
#! /bin/sh

[ -p /tmp/hogepipe ] || mkfifo /tmp/hogepipe # 名前付きパイプを作る

# 30分ごとに最新画像をダウンロードする
while [ 1 ]; do
  curl 'http://somewhere/beautiful_sight.jpg' > /tmp/hogepipe
  sleep 1800
done
せっかちな3人に画像をシェアしてあげるシェルスクリプト
#! /bin/sh

# 名前付きパイプからデータが到来し次第、3人のディレクトリにコピー
while :; do
  cat /tmp/hogepipe                                      \
  | tee /home/user_a/public_html/img/beautiful_sight.jpg \
  | tee /home/user_b/public_html/img/beautiful_sight.jpg \
  > tee /home/user_c/public_html/img/beautiful_sight.jpg
done

先のシェルスクリプトは30分毎にループしますが、後のシェルスクリプトはsleepせずにループします。ただし、大半はcatコマンドのところで先のシェルスクリプトがデータを送り出してくるのを待っているというわけです。

名前付きパイプを使わず、テンポラリーファイルで同じことをしようとするとちょっと大変 です。なにせ後のシェルスクリプトは、画像ファイルが最後までダウンロードし終わったことを何らかの手段で確認しなければならないのですから。いやぁmkfifoって、便利ですねー。

使用上の注意もいくつかある

気をつけなければならないこともあります。(2つ)

1つは、書き込む側のシェルスクリプトが

echo 1 > /tmp/hogepipe
echo 2 >> /tmp/hogepipe
echo 3 >> /tmp/hogepipe

のように、1つのデータを間欠的に(=オープン・クローズを繰り返しながら)送ってくる場合には使えません。クローズされた段階で、読み取り側は読み取りを止めてしまうからです。

もう1つは、何らかのトラブルで読み書きを終える前にプロセスが終了してしまった時です。ファイルなら途中経過が残りますが、名前付きパイプだと全て失われてしまいます。

まぁそんなこんながあって、面白い仕組みなんですけどなかなか使い道が無いんですよね。