for, while, untilのループ制御文は do〜 doneとで囲ま れるコマンド群を1つのコマンドのように実行しますのでループ全体の入出力をリダイ レクトしたり、パイプに接続することができます。バックグランドでの実行もループ全体 が対象となります。
次のリストは for文の標準出力をリダイレクトする例です。
listingsitem for i in beer carrot ell
do echo $i
done >food
ループ全体が1つのコマンドとして扱われますからリダイレクト文字 >
はループの終了を表す doneの後書きます。この時のリダイレクト対象はループ
内で標準出力に書き出すものすべてが対象となります。
しかし、ループ内でリダイレクト先が明示されているものはループ全体の
ものに優先してされます。例えば、上記のリストで
listingsitem for i in beer carrot ell
do echo "I like $i" >/dev/tty
echo $i
done >food
としたならば、3行目の出力は done >
food に優先して /dev/tty に
リダイレクトされます。(この場合には端末です)
同様に下記のようにパイプ記号 |
を doneに続けて書くことにより
ループの出力をパイプに流し込むこともできます。
listingsitem for i in beer carrot ell
do echo $i
done | food
ループ処理全体をバックグランドで実行させるにはループの終了を示す
doneの後ろにバックグランドへ送る指示のアンパーサンド &
を付けます。
例えば複数のソースからなるプログラムリストをバックグランドで連続紙に印刷
するには次のようにします。
listingsitem for i in *.[hc]
do pr -l66 -w132 $i | lpr
done &
forや while文などのリダイレクト処理はカレントシェルではなく、
サブシェルで実行される
ことに注意してください。
このことを知らないとバグでもないのにおかしな現象に悩まされることになります。
次のスクリプトは自分自身を行番号付きで表示するもので、
仮に "myself" と名付けておきましょう。
listingsitem n=0
while read line
do n=`expr $n + 1`
echo "$n: $line"
done < $0
echo "total line= $n"
これをmyselfとして実行してみてください。各行の先頭にふられた行番号は 正しく表示されていますが、最後に total line= 0 と表示され期待した結果が得ら れません。種明しをすると、ループ文の内側で行番号を表示するために 用いられているカウンタ nはサブシェル側の変数で、ループの外側にある変数 nはカレントシェルのものです。(このmyselfを起動したシェルから見ると 子、孫の関係になります)同じ名称の変数でもループの内と 外ではまったく別物ですから、 whileループの外側にある変数 nには スクリプトの最初で初期化されたままになっています。
ここで混同しないで頂きたいのですが、ループ文がサブシェルで実行されるのは
スクリプト内
でリダイレクト処理を指定したときだけです。
リダイレクトを指定しないループ文はカレントシェルで実行されます。スクリプトmyself
を次のように書き直してみてください。
listingsitem n=0
while read line
do n=`expr $n + 1`
echo "$n: $line"
done
echo "total line: $n"
そして、コマンドラインから "myself <
myself " とすると行数を数える
変数 nは期待する値を取ることで確認できます。さらに、入力をパイプから
読み込むよう、次のように書き直して変数 nの値を調べてみてください。
listingsitem n=0
cat $0 | while read line
do n=`expr $n + 1`
echo "$n: $line"
done
echo "total line: $n"