1. 概要

Bashスクリプトを使用すると、強力なツールを利用できます。 Bashスクリプトは、複数のコマンドを連続して実行するための優れた方法です。 スクリプトを作成するときは、1つのコマンドが失敗しても、後続のすべてのコマンドが実行されることを忘れないでください。 この記事では、いくつかのセーフガードを追加することでこれを防ぐ方法を学びます。 この記事の例で使用されているコマンドは、Bashでテストされています。 また、他のPOSIX互換シェルでも機能するはずです。

2. 問題

まず、bashスクリプトがデフォルトでエラーを処理する方法を見てみましょう。 「hello」と「world」という単語を出力する単純なスクリプトhello.shがあるとします。

#!/bin/bash
echo hello
echo world

それを実行すると、望ましい結果が得られます。

$ ./hello.sh
hello
world

次に、失敗することが保証されているステートメントを追加しましょう。

#!/bin/bash
echo hello
cat non-existing-file
echo world

このスクリプトを実行すると、エラーが出力されます。 ただし、実行は停止せず、「world」は引き続き出力されます。

$ ./hello.sh
hello
cat: non-existing-file: No such file or directory
world

また、スクリプトの終了コードがゼロになり、すべてが正常であることを示します。

$ echo $?
0

3. 最初のエラーで終了

発生した最初のエラーで、ゼロ以外の終了コードでスクリプトを終了させたいとしましょう。 したがって、 set を使用して、スクリプトの開始時にシェルのデフォルトの動作を変更する必要があります。

#!/bin/bash
set -e
echo hello
cat non-existing-file
echo world

最初の行にset-e を指定して、最初のエラーで実行を停止するようにBashに指示します。

$ ./hello.sh
hello
cat: non-existing-file: No such file or directory

また、返される終了コードは、失敗したコマンドの終了コードと同じです。

$ echo $?
1

4. pipefailを使用する

残念ながら、このソリューションはパイプステートメントを含むスクリプトでは機能しません。 たとえば、失敗したコマンドから始めて、最初の2つのコマンドをパイプでつなぎます。

#!/bin/bash
set -e
cat non-existing-file | echo hello
echo world

set -e を使用しましたが、出力は次のとおりです。

$ ./hello.sh
hello
cat: non-existing-file: No such file or directory
world

このシナリオを処理するために、最初の行のsetコマンドに追加オプションとして-opipefailを追加しましょう。

#!/bin/bash
set -eo pipefail
cat non-existing-file | echo hello
echo world

これにより、パイプ内でエラーが発生した場合、失敗した右端のコマンドの終了コードを停止して返す必要があることをBashに通知します。

$ ./hello.sh
hello
cat: non-existing-file: No such file or directory

これにより、前に見たのと同じゼロ以外の終了コードが生成されます。

$ echo $?
1

5. 結論

この記事では、 set -e を使用して、エラーが発生したときにbashスクリプトをすぐに終了させる方法を学びました。 また、 -o pipefail を追加して、パイプが同じように動作するようにすることも学びました。 スクリプトの最初の行として、常に set -eopipefailを追加することをお勧めします。