パイプを使うと最後の処理が成功すると戻り値は0になるので、`pipefail` を使ったほうが事故が少なくなるよというお話

コード例

#!/bin/bash -e

exit 1 | echo "a"
echo "b"

実行結果

a
b

-e オプションで、行単位で失敗したら終了するはずなのに、 echo "b" が実行されてしまっている...!

解説

pipeを使う場合、一番右側の処理の戻り値で行の成功/失敗が判定される

exit 1 | echo "a"
echo $? # <= 0

対策

set -o pipefail を使う

  • pipefail は、パイプの左側の処理で失敗した場合、戻り値を失敗扱いにする処理

コード例

#!/bin/bash -e

set -o pipefail

exit 1 | echo "a"
echo "b"

実行結果

a

パイプの右側も実行されるが、行全体としては失敗扱いとなり、 -e オプションにより処理が終了する

参考

DockerfileのLintツール hadolint でも、以下のようにして RUN 実行時に pipefail が設定されるように推奨している

SHELL ["/bin/bash", "-o", "pipefail", "-c"]

see: https://github.com/hadolint/hadolint/wiki/DL4006