bashのpipefailで確実にスクリプトを止める

Sun Apr 26 22:27:31 JST 2015 (modified: Sun Oct 1 10:50:27 JST 2017)
views: 11910, keywords:ごめんなさい,bash,Linux,Mac,pipefail,ご報告,シェルプログラミング実用テクニック,寝る この記事は最終更新日が6年以上前のものです。

シェルプログラミング実用テクニック、出版される前からもう補足ですが、私めがbashのpipefailというオプションをすっかり見落としていたのでフォローしておきます。

本文ではbashに-e(エラーがあったら止める)をつけてもパイプラインの左側のコマンドにエラーがあったときに処理が止まらないと書きました。

例です。

###false | true###でfalseが終了ステータス1を返すが・・・###
   uedambp:~ ueda$ cat hoge.bash 
   #!/bin/bash -e
   
   false | true
   echo do not stop
   ###-eがあるにもかかわらずechoが実行される###
   uedambp:~ ueda$ ./hoge.bash 
   do not stop

が、次のようにpipefailというオプションをセットしておくと(シバンの横には引数を一個しか渡せないと考えた方がよいので、下でsetを使って指定する)、次のように止まります。あらびっくり。

uedambp:~ ueda$ cat pipefail.bash 
   #!/bin/bash -e
   
   set -o pipefail
   
   false | true
   echo do not stop
   uedambp:~ ueda$ ./pipefail.bash 
   uedambp:~ ueda$ <- echoは実行されない

執筆する前にもうちょっとちゃんとmanをしっかり読んでおけよと自分に鋭いツッコミを入れましたが、bashの文法解説書というよりはコマンドの使い方解説書という感じで書いていたのでちょっと手薄になっておりました・・・。

manにはこう書いてあります。(Linuxだとmanの出力はパイプに渡せます。)

ueda@ubuntu:~$ man bash 2> /dev/null | grep -A 3 pipefail$
    pipefail
    設定されている場合、パイプラインの返り値は、 0 以外のステータスで終了した最後の
    (一番右の) コマンドの値になります。 パイプラインの全てのコマンドが成功の状態で
    終了すると 0 になります。 このオプションは、デフォルトで無効です。
   ueda@ubuntu:~$ LANG=C man bash 2> /dev/null | grep -A 3 pipefail$
    pipefail
    If set, the return value of a pipeline is the value of the last (rightmost)
    command to exit with a non-zero status, or zero if all commands in the pipeline
    exit successfully. This option is disabled by default.

しっかし、これ読んでも一回で正しい意味を読み取れるかどうか自信はありませぬ・・・。

もうちょい補足

見落としに気づいたのは、やっぱりbashの場合、PIPESTATUSが実装されているんだからスクリプトを止める方があるんじゃないかということで、校了後に不安になっていろいろ調べているうちに上記カンニング先で「あっ!」となりました。

「-eだと止まらないことがあるので自分で後始末は実装しましょう」みたいな文脈で-eを説明したのですが、pipefailがあるのでこれは知見の足らん説明となります。

trapと組み合わると、後始末ができてしまいます・・・。

uedambp:~ ueda$ cat pipefail_error.bash 
   #!/bin/bash -e
   
   set -o pipefail
   
   error () {
       echo "ERROR"
       #うまくエラーをトラップできればhogeが消える###
       rm hoge
   }
   
   #中間ファイルを作る###
   touch hoge
   
   trap error ERR
   
   false | true
   
   ###これ以後は実行されない###
   echo do not stop

やってみましょう。

uedambp:~ ueda$ ./pipefail_error.bash 
   ERROR
   ###ファイルが消えている###
   uedambp:~ ueda$ ls hoge
   gls: cannot access hoge: No such file or directory

ああああアホでした。自分で気がついてよかった。

ということで、本書のシェルスクリプトが少し冗長ということが判明しましたが、シェルスクリプトよりワンライナーの方が圧倒的に多いのと、大半のスクリプトは書捨てなので、これを念頭に読んでいただければ大丈夫かと思います。

以上。最後に宣伝。すんません・・・。

寝る。

ノート   このエントリーをはてなブックマークに追加 
 

prev:シェルプログラミング実用テクニックの目次が公開されました(エクシェル芸、斉藤さん、and 鳩) next:今の騒動に思うこと

やり散らかし一覧

記事いろいろ