タイトルのような挙動をするプログラムを作ってみたという記事です。
概要
lbyl というプログラムを作りました。(名前は line-by-line の略です)
↓こんな風に使います。
> ./long-lasting-script | lbyl growlnotify.exe ?
この例では、long-lasting-script からパイプされた標準入力を行単位で分割し、growlnotify の引数に渡しています。
末尾の「?」が引数の位置を指示するマーカーです。
lbyl の引数に渡されたプログラムは、標準入力の行ごとに入力を受け取った都度、個別のプロセスとして起動します。
- オプション
-throttling=xxx
でスロットリングしている場合は、指定のミリ秒ごとに入力を蓄積したうえでプロセスを起動します。 - オプション
-async
を使えば、プロセスを同時起動することもできます。 - オプション
-pipe
を指定すれば、引数ではなく、起動させたプロセスの標準入力に流し込みます。
背景
ユースケース
例えば、長時間かかるメンテナンス用のスクリプトを流していて、自分に都合の良いタイミングで状況を確認したいとします。
別の作業をしながら、ちらちらコンソールを見るというのも面倒です。
そこは、通知アプリを使って通知できるようにしたいものです。
ちなみに、メンテナンススクリプトは標準出力や標準エラー (コンソール画面) に状況を逐次出力しています。
また、コマンドラインから通知アプリにポストするコマンドは持っているものとします。(例: growlnotify.exe)
問題点
lbyl なしで単純にやろうとすると、こんな感じになるかもしれません。
> ./long-lasting-script | growlnotify.exe
ここで問題点が 2 つ。
-
growlnotify はパイプ (標準入力) からのメッセージの流し込みに対応していない。
-
パイプ (標準入力) を常に監視して都度処理をしてくれるプログラムって意外と少ない。
前者は xargs というものを使えば解決しそうです。私は Windows 使いですが、Windows でも類似のことはできそうです。
ただし…
後者については、以下を試してみてください。(Windows)
> ping 0.0.0.0 | more #これは逐次出力される
> ping 0.0.0.0 | msg * #最後まで処理されてから、まとめて表示される
> ping 0.0.0.0 | iconv -t utf-8 | more #要iconv まとめて表示される
要は、プログラムによって挙動がまちまち、ということです。
lbyl では
上の例は以下のように変わります。
> ping 0.0.0.0 | lbyl -pipe more
> ping 0.0.0.0 | lbyl -pipe msg *
> ping 0.0.0.0 | lbyl -pipe iconv -t utf-8 | more
lbyl から呼び出すことで行ごとの出力になります。
出力の開始から終了までに時間がかかるようなコマンドでも、逐次出力を得られるようになります。
開発
やっていることは結構単純です。
標準入力をバッファにためて、1行毎 (-throttle=0 の場合) もしくは指定時間後の読み込み時に (-throttle != 0 の場合) 引数に渡されたプログラムを起動するだけです。
ただし、ただ起動しただけでは、起動したプロセスの出力が lbyl の出力に流れません。
そのため、exec.Command() の戻り値の Stdout, Stderr に os.Stdout, os.Stderr を渡す形で出力を共有しています。
その他
lbyl は Go で作りました。
必要なライブラリが十分にそろっていて、ほとんど標準ライブラリで事足りました。
オプションの実装も、文字列操作も、プロセス起動も簡単に組み込めています。
また、Go の恩恵として、簡単にクロスコンパイルができました。
(Windows 以外の環境は、あまり使っていませんけどね)