リダイレクトとパイプを図示してみた (3)

前々回、前回の続きです。今回は、様々なリダイレクトやパイプを組み合わせた複雑なケースを説明します。
前回: id:simply-k:20100801:1280617927
補足: id:simply-k:20100803:1280796958

(11) コマンドA 2>&1 | コマンドB

リダイレクトとパイプを組み合わせたケースです。2段階に分割して考えます。

(11-1) 「コマンドA 2>&1」、「コマンドB」



パイプの前後のコマンドを単独実行する場合です。「コマンドA 2>&1」では、コマンドAの標準エラー出力を、標準出力にリダイレクトします。このため、コマンドAの標準出力のデータと標準エラー出力のデータは、まとめて標準出力(stdout-a)に出力されます。「コマンドB」は、リダイレクトの無い、通常のコマンド実行です。

(11-2) コマンドA 2>&1 | コマンドB


「コマンドA 2>&1」と「コマンドB」が、パイプによって接続されます。コマンドAの標準出力(stdout-a)とコマンドBの標準入力(stdin-b)が1つになったかのように動作します。コマンドAの標準出力のデータと標準エラー出力のデータは、まとめてコマンドBに受け渡されます。

(12) コマンドA 0< ファイルX 2>&1 1> NUL | コマンドB 2> NUL *1

リダイレクトとパイプを組み合わせたケースです。(11)よりも複雑ですが、考え方は同じです。このケースも、2段階に分割して考えます。

(12-1) 「コマンドA 0< ファイルX 2>&1 1> NUL」、「コマンドB 2> NUL」



パイプの前後のコマンドを単独実行する場合です。それぞれのコマンドで、標準入出力がリダイレクトされます。

(12-2) コマンドA 0< ファイルX 2>&1 1> NUL | コマンドB 2> NUL


「コマンドA 0< ファイルX 2>&1 1> NUL」と「コマンドB 2> NUL」が、パイプによって接続されます。データの流れは次のようになります。

  • コマンドAの標準入力には、ファイルからのデータが渡される。
  • コマンドAの標準出力から出力されるデータは、NULデバイスに渡される。(=捨てられる)
  • コマンドAの標準エラー出力から出力されるデータは、コマンドBの標準入力に渡される。(パイプ経由)
  • コマンドBの標準出力から出力されるデータは、画面に出力される。
  • コマンドBの標準エラー出力から出力されるデータは、NULデバイスに渡される。(=捨てられる)

(13) コマンド 3>&2 2>&1 1>&3

標準出力と標準エラー出力を入れ替えるケースです。3段階に分割して考えます。

(13-1) 3>&2


2番に結びつけられたストリーム(stderr)を3番に結び付けます。3番は、一時的に使用する出力です。*2

(13-2) 2>&1


1番に結びつけられたストリーム(stdout)を2番に結び付けます。

(13-3) 1>&3


3番に結びつけられたストリーム(stderr)を1番に結び付けます。この段階で、標準出力と標準エラー出力が入れ替わります。3番とstderrが結びついたままですが、コマンドが3番に出力することは無いので、無視することができます。
なお、「コマンド 3>&1 1>&2 2>&3」としても同じ結果となります。

(14) (コマンドA <ファイルX && コマンドB) 2> ファイルY

複数のコマンドを括弧でまとめ、1つのコマンドのように扱うケースです。*3

括弧で囲まれた部分を1つのコマンドとしてとらえると、次の図のように考えることもできます。

ここで、「コマンドC」の実装が「コマンドA <ファイルX && コマンドB」であると考えてください。
このような表記を使うと、複数のコマンドに対するリダイレクトをまとめて指定したり、複数のコマンドの出力をまとめて他のコマンドに渡せるようになります。


このシリーズは今回の記事で最後です。(2010/08/03追記 補足を書きました)
前回: id:simply-k:20100801:1280617927
補足: id:simply-k:20100803:1280796958

*1:Unix/Linuxでは、NULではなく/dev/nullとなります。

*2:一般的なシェルやコマンドプロンプトでは、0(標準入力)、1(標準出力)、2(標準エラー出力)の他に、3〜9が使えるようです。

*3:Unix/Linuxのシェルでは「サブシェル」、Windowsコマンドプロンプトでは「コマンドのグループ化」と呼ばれる機能です。