シェル備忘録

・リダイレクトとパイプ
どちらも入出力の宛先を変更する機能である。異なるのは、リダイレクトの場合は宛先がファイルであり、パイプの場合はプログラムの入力である点。
評価順序は

  1. パイプの評価 -> リダイレクトの評価
  2. 左から右


基本的なルールはこれだけなので難しい事はなさそうなのだが、いきなり
command1 2>&1 >/dev/null | command2
なんてのを見て、これが何を意味してるか分かる人ってそうはいない気がする。少なくとも俺はしばらく悩んだ。(苦笑


上記の例が何をやってるかを理解するには、「リダイレクトもパイプも要はファイルディスクリプタの書き換えである」と考えるといい。
ファイルディスクリプタとは、OSがファイルを扱うために使用する識別子の事。ファイル識別子とも呼ばれる。ファイルディスクリプタには、識別子、ファイル名、ファイルサイズ、ファイルの物理的な場所、作成日時、更新日時といった情報が含まれている。


まず、初期状態ではcommand1、command2のファイルディスクリプタ(以下、fd)とその内容は次のようになっている。数字はfd。

command1 0 stdin command2 0 stdin
1 stdout 1 stdout
2 stdout 2 stdout
さて、シェルの評価を順に追っていくと、まず初めにパイプが評価される。
command1 2>&1 >/dev/null  |  command2
その結果こうなる。
command1 0 stdin command2 0 A
1 A 1 stdout
2 stdout 2 stdout
Aというのは説明の便宜上つけただけなので実体は何?とかいう細かい事は気にしない。*1意味としてはcommand1の1とcommand2の0が繋がったと思ってくれればOK。俺はバカか。A はパイプじゃん。(苦笑
以下、順番に command1  2>&1  >/dev/null | command2
2を1と同じファイルに出力する。(=1の内容を2にコピー)
command1 0 stdin command2 0 A
1 A 1 stdout
2 A 2 stdout
command1 2>&1  >/dev/null  | command2
1の出力先を/dev/nullに変更する。
command1 0 stdin command2 0 A
1 /dev/null 1 stdout
2 A 2 stdout
という訳で、
command1 2>&1 >/dev/null | command2
がやっている事は「command1の標準出力は/dev/nullに捨て、標準エラー出力の内容をcommand2の入力値にする」でした。
なんてつらつらと書いてはみたが、あくまで俺の理解なので間違っている可能性がある事をお断りしておく。なお、ツッコミは大歓迎ですよ。

><

*1:俺もなんだかよく分かってないしな