動機
あんまり機会/需要はないと思うが、あるコマンドの出力を複数のディレクトリに置きたくなることがあった。
基本的には、tee
コマンドを使えばいいのだが、標準出力/標準エラー出力を分けたり、さらなる別コマンドにパイプできるようにfanout.sh
としてスクリプト化した。別記事にあるように、source
しても使えるように実装した。
ファイル置き場
基本的な使い方
ヘルプ表示
% fanout.sh [options] [-o file] [-e file] [cmd [cmd_options .... ]][Options]
-k : Keep stdout/stderr (default)-d : No stdout/stderr output
-a : append to existing files
-m : merge stdout and stderr outputs.
-o path : file name to dump stdout
-e path : file name to dump stderr
-h : Show Help (this message)
コマンドライン引数cmd [cmd_options]
を実行し、標準出力を-o
オプション引数で指定されたファイルに、標準エラー出力を-e
オプションで指定されたファイルに保存する。
-o
オプションは、-e
オプションは複数指定できる。(これがこのスプリプトのご利益)
コマンドライン引数cmd [cmd_options]
が指定されなかった場合には、標準入力からの入力を表示する。(つまりcat
コマンドが呼ばれる。)
-o
もしくは-e
オプションが1つも指定されない場合には、単にコマンドを実行する。
その他のオプションの紹介
-k
: 実行したコマンドの出力/エラー出力をさらに標準出力(fd:1
)/標準エラー(fd:2
)に出力する。(デフォルトの動作)-d
:-o
オプションが指定されていれば、実行したコマンドの出力はファイルにリダイレクトし、このコマンドの標準出力(fd:1
)には出力しない。-e
オプションが指定されていれば、実行したコマンドのエラー出力はファイルにリダイレクトし、このコマンドの標準エラー出力(fd:2
)には出力しない。(-k
オプションの逆)-m
:-o
オプション,-e
オプションで指定されたファイルに実行したコマンドの出力とエラー出力の両方をまとめてリダイレクトする。-a
:-o
オプション,-e
オプションで指定されたファイルに追記する。(tee -a
,リダイレクション>>
に相当)
スクリプトの中身
- 中身は
tee
とリダイレクションをしているだけのbash
スクリプトである。 エラー出力をtee
を用いて複数ファイルに保存するため、サブシェルでファイルディスクリプタ複製を行なっている。下記のように
例
% ((cmd cmd_options | tee o1 | tee o2 ) 3>&2 2>&1 1>&3 | tee e1 | tee e2 ) 3>&2 2>&1 1>&3 1>o0 2>e0
とすると、cmd cmd_options
の実行結果の出力をo1
,o2
に出力し、ファイルディスクリプタを切り替えて実行結果のエラー出力をe1
,e2
にリダイレクトしたのち、もう一度ファイルディスクリプタを切り替えて元に戻す、といったことを行う。
-m
オプションと-k
オプションが同時に指定された場合には、実行コマンドの出力とエラー出力をまとめて保存しつつ、標準出力(fd:1
)/標準エラー出力(fd:2
)にはまとめずに出力/エラー出力をそれぞれ出すようにするため、名前付きパイプを用いている。下記のような例では、cmd cmd_options
の実行結果の出力とエラー出力を名前付きパイプを用いてそれぞれ標準出力(fd:1
)、標準エラー出力(fd:2
)に出力しつつ、実行結果の出力とエラー出力をまとめてファイルm1
,m2
にリダイレクトする.
例
% mkfifo sobuf
% mkfifo sebuf
% cat< sobuf &
% cat< sebuf 2>&1 &
% ((cmd cmd_output | tee sobuf ) 3>&2 2>&1 1>&3 | tee sebuf ) 2>&1 | tee m1 > m2
% rm sobuf sebuf
実装
-d
/-k
オプションの切り替えなどは/dev/null
をうまく使えば、スクリプトとしてはもっとシンプルに書けるとは思うが、不必要なリダイレクションは行わないように愚直に場合分けした。