62
31

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-09-13

はじめに

主に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 を使う際はお気をつけ下さい。

参考

62
31
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
62
31

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?