script コマンドを使った小技です。
(使っている Linux は Ubuntu 14.04.3 LTS です)
bash には 「プロセス置換」という機能があります。
Process Substitution Process substitution is supported on systems that support named pipes (FIFOs) or the /dev/fd method of naming open files. It takes the form of <(list) or >(list). The process list is run with its input or output con‐ nected to a FIFO or some file in /dev/fd. The name of this file is passed as an argument to the current com‐ mand as the result of the expansion. If the >(list) form is used, writing to the file will provide input for list. If the <(list) form is used, the file passed as an argument should be read to obtain the output of list. When available, process substitution is performed simultaneously with parameter and variable expansion, com‐ mand substitution, and arithmetic expansion.
処理中の一時ファイルの作成を減らせる機能で、 無くても他の方法で代用は効くのですが 良い感じで使うとコマンドが読みやすくなったりします。
書き方は、次の 2つ。
<(コマンド)
>(コマンド)
名前の似てる「コマンド置換」と書き方も似てますね。
$(コマンド) #コマンド置換
「コマンド置換」は、コマンドの結果を コマンドやコマンドの引数として使用することができました。
$ echo $(echo date) date
$ $(echo date) Thu Dec 8 22:58:19 JST 2018
「プロセス置換」は、コマンドの結果を ファイルの入力のように扱ったり 出力をコマンドに渡したりできます。
まずは "<(コマンド)" の方ですが、コマンドで入力ファイルのパスを 指定するところに書くことができます。
よく使われる例としては
diff コマンドです。
diff コマンドは、2つのファイルを比較する際に
標準出力が 1つしか指定できないため
2つのコマンドの実行結果を比較したい場合
比較するコマンドの片方は実体が必要になります。
$ ls -l /var/xxxx > xxxx.txt $ ls -l /var/yyyy > yyyy.txt $ diff xxxx.txt yyyy.txt
$ ls -l /var/xxxx > xxxx.txt $ ls -l /var/yyyy | diff - xxxx.txt
「プロセス置換」を使うと 一時ファイルを作成せずに 2つのコマンドの実行結果を そのまま比較することができます。
$ diff <(ls -l /var/xxxx) <(ls -l /var/yyyy)
コマンドも見やすいですね。
一時ファイルを削除する手間もなくなります。
echo で出力すると 「プロセス置換」の実体が ファイルデスクリプタだとわかります。
$ echo <(echo 1)
/dev/fd/63
わかりにくくなりますが 次のようなこともできます。
$ $(cat <(echo date))
Thu Dec 8 22:14:36 JST 2018
これは まず 次のように処理されて "date" が返ってきます。
$ echo date > /dev/fd/63 $ cat /dev/fd/63
返ってきた date を実行して 日時が出力されました。
$ date
Thu Dec 8 22:14:36 JST 2018
また 次のような do ... done のループで パイプでつなぐと 中の処理が別プロセスになり 環境変数を 上書きしてくれないような場合、、、
$ filename=none $ ls | grep -v "test." | while read filepath > do > filename=$(basename $filepath) > done $ echo $filename none #←別プロセスのため上書きされない
「プロセス置換」を使うと ファイルを指定するのと同じように 同じプロセスで処理することができます。
$ filename=none $ while read filepath > do > filename=$(basename $filepath) > done < <(ls | grep -v "test.") $ echo $filename xxxxx.txt #←同じプロセスのため上書きされた
次に ">(コマンド)" の方ですが、こちらは だいたいパイプでできてしまうため あまり良い例が思い浮かびませんでした。
よく使うのは次のように 標準エラー出力で 不要な行をオミットするケースすです。
$ command 2> >(grep -v ^Notice: >&2)
これは、標準エラー出力を一旦 プロセス置換で grep コマンドに渡して "Notice:" から 始まる行を省いて、再度標準エラー出力に渡しています。
他には tee コマンドと組み合わせて 特定の出力だけ振り分ける なんてことができます。
$ cat test.txt | \ > tee >(grep ^case1 > case1.txt) \ > tee >(grep ^case2 > case2.txt) \ > tee >(grep ^case3 > case3.txt) \ > > /dev/null
この例では、出力された行の先頭の文字によって 別のファイルに保存しています。