シェルのリダイレクション

昨日に続いて,UNIXとの違いについて小ネタ.bashで,標準出力(stdout),標準エラー出力(stderr)を/dev/nullに出力したい場合や,makeでstdout,stderrの両方をログに残したい場合には次のように書く.

bash% cmd > /dev/null 2>&1
bash% make 2>&1 | tee make.log

この挙動は,「シェルのリダイレクトにまつわる失敗」などで書かれているように,リダイレクションはdupを使って実装されていることを知らないとわかりにくい*1.上の例で示した"2>&1"は,実装としてはdup(1, 2)に対応し,stderr (fd=2)をいったんcloseして,stdout (fd=1)の複製をstderrに設定する,つまりstderrの出力先がstdoutになることを意味する.

今日はrcではリダイレクションがどのように扱われるか見ていこう*2
まず,単純なstderrのリダイレクション例から.bashでは,リダイレクション記号の">"に対象となるファイル記述子を前置するのに対して,rcでは"[""]"で囲んで後置する.

bash% cmd 2> cmd.err
rc  % cmd >[2] cmd.err

では,最初に示した2つの例はどのように書くかと言うと,

rc  % cmd > /dev/null >[2=1]
rc  % mk >[2=1] | tee mk.log

となる."2=1"はstderrをstdoutに置き換えることを意味する.このようにrcの方がリダイレクションを直感的に書けるようになっている.

さらに,この考えの延長線でパイプの記法も拡張されている.例えば,stderrだけをパイプ以降のコマンドに渡したい場合は,"|[2]"と書けばよい.

rc  % cmd |[2] tee foo.err

*1:UNIXプログラミングの定番の勉強法は,簡単なシェルを作ることである.最低限,リダイレクションとパイプは実現したい.

*2:rcはUNIXにもポートされている.rcのコードはUNIXPlan9の違いを知るよい題材かもしれない.