ShellScript
Bash
Linux
shell
sh

シェルスクリプトで複数のコマンド出力を結合する

やりたいこと

SoftwareDesignのシェルスクリプト記事を読んでから、シェルスクリプトがちょっとブーム。
いっぱいパイプにつないで喜んでたら、ふと疑問が浮かんだ。
コマンド出力の分岐は伝家の宝刀teeコマンドがある。
じゃぁ結合は?

方法1 一時ファイルに書く

$ TMP=`tempfile`; echo hoge > $TMP; echo fuga >> $TMP; cat $TMP; rm $TMP

まぁ、できてるけども。。。

方法2 bashのプロセス置換を使う

$ cat <(echo hoge) <(echo fuga)
hoge
fuga

スマート。
でもbash限定。
そして出力が継続するコマンド(何て呼べばいいんだろう)に対してはうまく行かない。
(追記:"a continuous streaming command"と言うそうです。@7of9さん、ありがとうございます。)

方法3 fifoを使う

$ mkfifo /tmp/shout
$ echo hoge > /tmp/shout | echo fuga > /tmp/shout | cat /tmp/shout
hoge
fuga

fifoだと入力があるまでブロックしてくれるので、バックドア的な使い方ができる。
例えば、標準入出力しかI/FがないインタラクティブなCUIアプリ(QEMUのようなシミュレータとか)に対して、CIから自動テストのトリガをかける時などに使える。

1. CUIアプリ起動時に標準入力を/tmp/shuoutを経由するようにしておく
(CUIアプリの例として下記ではbashにしている)

$ mkfifo /tmp/shout
$ cat > /tmp/shout | cat /tmp/shout | bash
uname -r
4.13.0-32-generic

2.別のターミナルから、/tmp/shoutにコマンドを流しこむ

$ echo uname -r > /tmp/shout

3. 1.で起動したCUIアプリにコマンドが入力される
(今回の例ではbash上でコマンドが実行される)

4.13.0-32-generic

手順2.でnc -lkなどと繋げば他のホストから入力することも可能。
もちろんecho $'\cC' > /tmp/shoutなどとしたら制御文字を送りつけることもできる。

結論

bashで単発のコマンド出力を結合するときは、プロセス置換。
bashじゃない、または終わらないコマンド("a continuous streaming command")の出力を結合するときは、fifo。

雑感

シェルスクリプトは限られたコマンドを組み合わせて目的を達成するパズル的な楽しさがあるなー。

参考