yn2011's blog

技術メモ

シェルスクリプトの出力を画面に表示しつつファイル出力する方法について

環境

ターミナル上の作業ログを取りたい場合

  • scriptコマンドを使うとターミナル上の操作と出力を全てファイルに記録できる
$ script
Script started, output file is typescript
$ echo hoge
hoge
$ ls hoge
ls: cannot access 'hoge': No such file or directory
$ exit
// typescript
Script started on Sun Dec  2 19:15:10 2018
bash-4.4$ echo hoge
hoge
bash-4.4$ ls hoge
ls: cannot access 'hoge': No such file or directory
bash-4.4$ exit
exit

Script done on Sun Dec  2 19:15:45 2018

実行するシェルスクリプトのログを画面に表示しつつファイル出力したい場合

exec 1> >(tee -a stdout.log)
exec 2> >(tee -a stderr.log)

# 処理
echo hoge
  • なる...ほど?
  • シェルスクリプトに慣れ親しんでいないと分かりにくい(少なくとも自分は理解できなかった)
  • 分解して考えてみる

リダイレクト

  • 始めにリダイレクトの概念を知っている必要がある

リダイレクトは標準入出力の入力元、出力先を置き換える機能のこと

[三宅 英明 (2017) . 新しいシェルプログラミングの教科書 SBクリエイティブ P115より引用]

  • 例えばecho hoge 1> hoge.logは標準出力をhoge.logにリダイレクトする
  • 1はファイルディスクリプタの番号
  • 1>を省略すると>になるのでecho hoge > hoge.logでも良い

execコマンド

  • 続いてexec

execに指定したコマンドを実行する。このコマンドを実行する際に,新しいプロセスを作成せずに,現在のジョブと置き換えて実行される。...また,何もコマンドを指定せずにリダイレクトを利用すると,現在のシェルのリダイレクトを切り替えられる。

【 exec 】 現行のジョブに置き換えてコマンドを続行する | 日経 xTECH(クロステック)より引用

  • ここではコマンドを指定していないのでシェルのリダイレクトを切り替えている

プロセス置換

  • <(command)>(command)の2つがある

<(command)

  • <(command)は、プロセス置換によって割り当てられたファイルにcommandの標準出力を出力
  • シェル芸でも時々使う
$ echo <(echo test)
/dev/fd/63

$ cat <(echo test)
test

>(command)

  • >(command)は、プロセス置換によって割り当てられたファイルの内容を標準入力としてcommandを実行
    • execと組み合わせることが多い模様
    • 今回はシェルの出力が、一時的に割り当てられたファイルへリダイレクトされ、それを標準入力としてコマンドが実行される

teeコマンド

  • 最後にtee

    tee(ティー)はUnixのコマンドの一つ。 コマンドの標準出力 (stdout)を他のファイルにコピーできる機能を提供する。

tee (UNIX) - Wikipediaより引用

  • 標準出力しつつファイルにも出力する
  • 処理の流れがT字型のイメージ
  • -aはファイルに追記のオプション
$ echo hoge | tee hoge.log
hoge

$ cat hoge.log
hoge

これらを踏まえて

  • もう1度見てみる
exec 1> >(tee -a stdout.log)
exec 2> >(tee -a stderr.log)
  • 現在のシェルの標準出力と標準エラー出力をそれぞれteeの標準入力にすることで画面に出力しつつファイルへの記録を実現している
  • 処理の意味が理解できた!(と思う)

最後に


  1. 元記事のexec 2> >(tee -a stderr.log >&2)>&2はどうして必要なのかよく分からなかった。知っている方いたら教えてください