LoginSignup
8
8

More than 5 years have passed since last update.

シェルスクリプトのwhile readループが悪いのか?回避策は?

Last updated at Posted at 2016-03-06

以下の投稿、根本的な間違いがあったのであとで直します。(shと思ってたものが、実はbashだったとは、、)

シェルスクリプトのwhile readループ問題

シェルスクリプトの鬼門とも言うべきwhile readループの問題を少しまとめたい。
たとえばpsコマンドの結果を画面に表示して、最後に個数を表示したとする。

root_process.sh
#!/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でも動く方法があったので紹介する。

root_process_2.sh
#!/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ページにいくつかあるが、いずれも少し特殊な書き方のように思う。

8
8
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8