dogwood008の開発メモ!

最近のマイブームは機械学習, Ruby on Rails。中でも機械学習を使った金融商品の自動取引に興味があります。

【Bash】tee でstdoutとstderrをそれぞれ別々にする方法 (Process Substitution)

要旨

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)

参考

www.gnu.org

qiita.com