要旨
Bashで用意されているプロセス置換 (Process Substitution
) という仕組みを使うと、下記のような書き方ができる。
$ ls /foo/bar 1> >(tee stdout.log) 2> >(tee stderr.log) ls: /foo/bar: No such file or directory # <--- 標準エラー $ cat stderr.log ls: /foo/bar: No such file or directory # <--- `./stderr.log` にも書き込まれた
背景
Bashで標準出力 (stdout
)と標準エラー (stderr
)をそれぞれファイルに出力する場合、次のように記述することで可能である。
$ ls /foo/bar > stdout.log 2> stderr.log $ cat stderr.log ls: /foo/bar: No such file or directory
ところが、これを tee
に適用しようとすると、なかなか難しい。下記はうまく動かない例。
$ ls /foo/bar 2> tee stderr.log | tee stdout.log $ cat tee # <--- teeコマンドを呼ぶのではなく、 `./tee` というファイルに書き込まれた ls: /foo/bar: No such file or directory ls: stderr.log: No such file or directory
要旨に記載したプロセス置換を使うと、下記のように記述できる。
$ ls /foo/bar 1> >(tee stdout.log) 2> >(tee stderr.log)
応用
私は主に、この仕組みをRSpecの実行結果を特定の位置に出力させるために用いている。各Project毎のRSpecの設定を確認しなくても、毎回定位置を見れば必ずログが出力されていることが保証されながら画面にもアウトプットされて便利である。
下記を実行すると、そのログが次の2つのパスへ出力される。その際、実行時の日時がファイル名として記録される。
rspec/yyyy-mm-dd-HH-MM-SS_some_spec.rb.log
rspec/yyyy-mm-dd-HH-MM-SS_some_spec.rb.err.log
FILE=spec/path/to/some_spec.rb; OUTPRFX=`echo _$(basename ${FILE}.log)`; time RAILS_ENV=test bundle exec spring rspec ${FILE} 1> >(tee rspec/`date "+%Y-%m-%d-%H-%M-%S"`${OUTPRFX}) 2> >(tee rspec/`date "+%Y-%m-%d-%H-%M-%S"`${OUTPRFX}.err.log)