以下の投稿、根本的な間違いがあったのであとで直します。(shと思ってたものが、実はbashだったとは、、)
シェルスクリプトのwhile readループ問題
シェルスクリプトの鬼門とも言うべきwhile readループの問題を少しまとめたい。
たとえばpsコマンドの結果を画面に表示して、最後に個数を表示したとする。
#!/bin/sh
declare -i COUNTER=0
ps -ef|awk '$1=="root"'| while read LINE
do
COUNTER+=1
set -- $LINE
echo $COUNTER : $8
done
echo COUNTER=$COUNTER
これを実行すると、最後の個数表示がCOUNTER=0
となってしまう。
同じように、while do ~ doneループ内でexitしてもうまく動いてくれないなど、whileループには問題がいくつかある。
悪いのは誰?
上記の例の場合にwhileループを避けてawkなどで書き直せばうまく行くのだが、この問題の原因はwhileループなのではなく、シェルのパイプライン処理は子プロセスで実行するという仕様によるものである。
(ksh,zshの場合にはパイプラインの末尾は親シェルの一部として実行する。bashではshopt -s lastpipe
とすることで同様のことが可能。)
回避策
whileループを使って、うまく避ける方法がないかと考えて試行錯誤したが、どうやらBourne Shellでも動く方法があったので紹介する。
#!/bin/sh
declare -i COUNTER=0
while read LINE
do
COUNTER+=1
set -- $LINE
echo $COUNTER : $8
done <<EOF
$(ps -ef|awk '$1=="root"')
EOF
echo COUNTER=$COUNTER
書いてしまえば何でもないのだが、少しだけコツがあった。
1.パイプラインでwhileに渡さずに、ヒアドキュメントにする。
2.ヒアドキュメント内で子プロセス実行($()まはた``)で実行してやる。
これでBourne Shellでも動く。bash特有の書き方はstack over flowページにいくつかあるが、いずれも少し特殊な書き方のように思う。