Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

シェルスクリプトで "set -o pipefail" を付けるとSIGPIPEで死ぬ

More than 1 year has passed since last update.

はじめに

主にLinux環境で動くBashスクリプトについて書きます。

シェルスクリプトで、パイプを使ったコマンドを書く時に、安全弁として set -o pipefail を付けるのは良いマナーだと思います。
これにより、パイプの左側のコマンドが失敗したとき、スクリプトをそこで停止することができます。

pipefailの動作例
#!/usr/bin/env bash

set -eo pipefail

false | true
echo "won't be printed" # この行は実行されない

false コマンドは常に失敗するので、以降の行は実行されないというわけです。

SIGPIPEで死ぬパターン

しかし、ここで1つ落とし穴があります。
パイプの左側のコマンドが通常は成功する場合でも、パイプの右側のコマンドによって SIGPIPE が発生することがあるのです。

例えば、次のようなパターンです。

SIGPIPEで死ぬケース
# 例1
cat some-big-file | head -n 10

# 例2
cat some-big-file | grep -q foo

この場合も、スクリプトは停止し、以降の処理は実行されません。

どうやら、上のようにパイプの右側で headgrep -q コマンドを実行した際、パイプがクローズされてしまい、クローズされたパイプへの書込みが発生することで SIGPIPE が発生すると推測しています。

回避策

trapコマンドなどを使ってスマートに対処できないか試しましたが、残念ながら上手く行きませんでした。
ワークアラウンドなやり方になりますが、以下に挙げておきます。

①SIGPIPEが発生しないようにコマンドを書き換える

上の例1, 2の場合、次のように書き換えられます:

回避策①
# 例1改
tac some-big-file | tail -n 10

# 例2改
cat some-big-file | grep foo &>/dev/null

②コマンドがエラーにならないようにする

例えば次のようにします:

回避策②
cat some-big-file | head -n 10 || true

|| true の効果で、このワンライナーは絶対に失敗しなくなります。
|| : などでも良いですね。

set -o pipefail を止める

そもそも使わないという選択もあり得ますが、部分的に無効化することもできます。

回避策③
set +o pipefail # オプション無効化
cat some-big-file | head -n 10
set -o pipefail # オプション再有効化

まとめ

set -o pipefail を使う際はお気をつけ下さい。

参考

progrhyme
Software Engineer. Was @key-amb
https://progrhy.me/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away