Subject: Why doesn't redirecting a loop work as intended? (Bourne shell) Date: Thu Mar 18 17:16:55 EST 1993
foo=bar while read line do # do something with $line foo=bletch done < /etc/passwd echo "foo is now: $foo"多くの Bourne シェルの実装では、``foo=bletch'' としたのに ``foo is now: bar'' と出力されてしまいます。なぜでしょう。これは以下の ような、あまり文書化されていませんが、歴代のBourneシェルの仕様のせいで す。(ループや if 文などの) 制御構造をリダイレクトするとサブシェルがつく られ、その中で制御構造が実行されます。だから(``foo=bletch'' のように)サ ブシェルでセットされた変数は、カレントのシェルには、当然、影響を与えま せん。
POSIX 1003.2 の Shell and Tools Interface 標準化委員会は上記のような動 作を禁止しています。つまり、P1003.2 準拠の Bourne シェルは、この例の場 合``foo is now: bletch''と出力するでしょう。
歴代の (そして P1003.2 準拠の) 実装で、このリダイレクション問題を回避す るためには、次のような「トリック」が使えます。
foo=bar # ファイル記述子9をファイル記述子0 (標準入力) の複製にする。 # その後、標準入力を /etc/passwd に接続する; もとの標準入力は # ファイル記述子9に「記憶」されている; dup(2)、sh(1) を参照。 exec 9<&0 < /etc/passwd while read line do # $line に対して何かする。 foo=bletch done # 標準入力をファイル記述子9の複製にする。つまり、標準入力を # もとの標準入力に接続し直す; その後、ファイル記述子9を # クローズする。 exec 0<&9 9<&- echo "foo is now: $foo"常に``foo is now: bletch''を出力するでしょう。よろしい、では次の例です。
foo=bar echo bletch | read foo echo "foo is now: $foo"これは多くの実装では、``foo is now: bar''と出力されますが、 ``foo is now: bletch'' と出力されるものもあります。なぜでしょうか。一般 的には、パイプラインの各部は別々のサブシェルで実行されます。しかし、い くつかの実装では、パイプラインの最後のコマンドについては例外となってい ます。パイプラインの最後のコマンドが ``read'' のような組み込みコマンド だと、カレントシェルによって実行され、そうでなければ、サブシェルが作ら れるようになってる場合があるからです。
Posix 1003.2 はどちらの動作も許しているので、移植性のあるスクリプトにす るには、この動作に依存しないようにして下さい。