プロセス置換とリダイレクションでプログレスバーを操作

上記日記からの続き。

  • ところで、CocoaDialogの実例には、プロセス置換とリダイレクションを利用した方法が紹介されている。(なぜ、単純にパイプを用いないのだろう?)
for (( i = 1; i <= 100; i++ )); do
    echo $i $i/100 running...; sleep .05;
done > >(/Applications/CocoaDialog.app/Contents/MacOS/CocoaDialog progressbar)
  • パイプとの違いは...
 - done |   /Applications/CocoaDialog.app/Contents/MacOS/CocoaDialog progressbar
 + done > >(/Applications/CocoaDialog.app/Contents/MacOS/CocoaDialog progressbar)
  • >(/Applications/CocoaDialog.app/Contents/MacOS/CocoaDialog progressbar) の部分がプロセス置換と呼ばれている。
  • lsコマンドで探ってみると...
$ ls >(/Applications/CocoaDialog.app/Contents/MacOS/CocoaDialog progressbar)
/dev/fd/63
  • つまり、forブロックの内の標準出力は、/dev/fd/63 というデバイスファイルに接続されるのだと思う。(for do ... done > /dev/fd/63)
  • そして、/dev/fd/63 に送信されたデータは /Applications/CocoaDialog.app/Contents/MacOS/CocoaDialog progressbar への入力となり、プログレスバーが更新される。
  • パイプの場合は「forブロック内の標準出力」と「.../CocoaDialog progressbarの標準入力」が繋がっていた。
  • 一方、プロセス置換とリダイレクションでは「forブロック内の標準出力」と「.../CocoaDialog progressbarの/dev/fd/63」が繋がっている。
    • プロセス置換によって、.../CocoaDialog progressbar/dev/fd/63という入口が設定されるのだ。

まめ知識 プロセス置換

  • 上記の例は入力側のプロセス置換 >(コマンド) だが、
  • 逆に、出力側のプロセス置換 <(コマンド) もある。
  • 例えばgzip圧縮されたファイル同士のdiffを確認したい場合、そのままでは差分を確認できない。
$ diff -u hello1.sh.gz hello2.sh.gz
Binary files hello1.sh.gz and hello2.sh.gz differ
  • よって、通常はgzipファイルを一旦解凍して、テキストファイルに変換してから、diffを実行する。
$ gzip -d hello1.sh.gz
$ gzip -d hello2.sh.gz
$ diff -u hello1.sh hello2.sh
  • しかし、解凍してから比較するのは面倒くさい。
  • そんな時に、プロセス置換を知っていると便利。
 $ diff -u <(gzip -cd hello1.sh.gz) <(gzip -cd hello2.sh.gz)
 --- /dev/fd/63	2013-07-05 14:25:36.000000000 +0900
 +++ /dev/fd/62	2013-07-05 14:25:36.000000000 +0900
 @@ -1,3 +1,3 @@
  #!/bin/bash
 
 -echo hello
 +echo Hello, World!!
  • gzip -cdコマンドは、.gzファイルを解凍して標準出力に表示する。
$ gzip -cd hello1.sh.gz
#!/bin/bash

echo hello
  • プロセス置換は、標準出力の内容を1つのファイルのように扱うのだ。
  • プロセス置換をlsしてみると、2つのデバイスファイルになる。
$ ls <(gzip -cd hello1.sh.gz) <(gzip -cd hello2.sh.gz)
/dev/fd/62 /dev/fd/63
  • つまり、<(gzip -cd ...)の部分は、以下のように置き換えられるのだ。
$ diff -u /dev/fd/62 /dev/fd/63
  • /dev/fd/62 によって、gzip -cd hello1.sh.gzで解凍した内容にアクセスできる。
  • /dev/fd/63 によって、gzip -cd hello2.sh.gzで解凍した内容にアクセスできる。
  • /dev/fd/62/dev/fd/63 は、まるでファイルのようだけど、ハードディスク上に保存される訳ではない。
  • /dev/fd/62/dev/fd/63 でアクセスされるのは、メモリ上に展開されたデータである。
  • diffと2つのgzipコマンドは、パイプの時と同じようにマルチタスク的に起動している。
  • そして、/dev/fd/62/dev/fd/63 といったデバイスファイルを通して、解凍されたデータにアクセスしているのである。
<(コマンド) コマンドの処理結果を取得する出口のパスに置き換えられる
>(コマンド) コマンドへデータを渡すときの入口のパスに置き換えられる
  • 例:
$ echo >(command)
/dev/fd/63
  • イメージ
$ diff -u <(gzip -cd hello1.sh.gz) <(gzip -cd hello2.sh.gz)

+-----------------------+            +-----------------------+
|                       |------------|                       |
|                       ← /dev/fd/62 ← gzip -cd hello1.sh.gz |
|                       |------------|                       |
|                       |            +-----------------------+
|          ls           |                                     
|                       |            +-----------------------+
|                       |------------|                       |
|                       ← /dev/fd/63 ← gzip -cd hello2.sh.gz |
|                       |------------|                       |
+-----------------------+            +-----------------------+

$ ls <(gzip -cd hello1.sh.gz) <(gzip -cd hello2.sh.gz)
/dev/fd/62 /dev/fd/63