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 はどちらの動作も許しているので、移植性のあるスクリプトにす るには、この動作に依存しないようにして下さい。