Subject: Why doesn't redirecting a loop work as intended?  (Bourne shell)
Date: Thu Mar 18 17:16:55 EST 1993

3.8) なぜループの出力をリダイレクトしても思いどうり動かないのか (B-shell)。

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


UNIX FAQ LIST / Copyright(c)1994,Ted Timar / tmatimar@isgtec.com


Maintainer: あさだ たくや