パイプでつないだwhile文内のexit 1

UNIXコマンドとwhile文をパイプでつなぐケースでエラー時にリターンコードを返して異常終了させようとするときの注意点です。

#!/bin/sh

echo "debug:10"
cat hoge.txt | while read LINE
do
  echo "debug:20"
  exit 1
  echo "debug:30"
done
echo "debug:40"

期待する挙動はこうですが

debug:10
debug:20

実際の挙動はこのようになります。

debug:10
debug:20
debug:40

catの結果をパイプで受け取ったwhile文はスクリプト実行のプロセスの小プロセスとなり、その小プロセス内のexit 1は自身を異常終了するだけなので、このような挙動になるようです。スクリプト実行の親プロセスは生きていて、子プロセスが死んだあとも以降の処理を継続するのです。

意図する挙動をさせるためには、以下のように小プロセスの異常終了を受け取って親プロセスも異常終了させます。

#!/bin/sh

echo "debug:10"
cat hoge.txt | while read LINE
do
  echo "debug:20"
  exit 1
  echo "debug:30"
done
if [ $? -ne 0 ]; then
  exit 1
fi
echo "debug:40"

ただし、一部のkshでは小プロセスのexit 1で親プロセスまで異常終了する場合もあるようです。

このようなケースを確認した環境はRed Hat(2.6.9-34.EL/ia64)なのですが、ディストリビューション依存なのかインストールされているkshのバージョン?によるものなのかは不明です。