LinuxのBashスクリプトでsetとpipefailを使用する方法

公開: 2022-06-25
青い背景の上のラップトップ画面上のLinux端末。
fatmawati achmad zaenuri / Shutterstock.com

Linuxのsetコマンドとpipefailコマンドは、Bashスクリプトで障害が発生したときに何が起こるかを指示します。 やめるべきか、継続すべきかということ以上に、考えることがたくさんあります。

関連:シェルスクリプトの初心者向けガイド:基本

Bashスクリプトとエラー条件

Bashシェルスクリプトは素晴らしいです。 それらはすぐに書くことができ、コンパイルする必要はありません。 実行する必要のある反復または多段階のアクションは、便利なスクリプトでラップできます。 また、スクリプトは標準のLinuxユーティリティのいずれかを呼び出すことができるため、シェル言語自体の機能に制限されません。

ただし、外部ユーティリティまたはプログラムを呼び出すと、問題が発生する可能性があります。 失敗した場合、外部ユーティリティは閉じてリターンコードをシェルに送信し、端末にエラーメッセージを出力することもあります。 ただし、スクリプトは処理を続行します。 おそらくそれはあなたが望んでいたことではありません。 スクリプトの実行の早い段階でエラーが発生した場合、スクリプトの残りの部分の実行が許可されていると、問題が悪化する可能性があります。

Bashシェルとは何ですか?Linuxにとってなぜそれほど重要なのですか?
関連するBashシェルとは何ですか?Linuxにとってなぜそれほど重要なのですか?

完了時に各外部プロセスからの戻りコードを確認できますが、プロセスが他のプロセスにパイプされると、それは困難になります。 戻りコードは、失敗した途中のプロセスではなく、パイプの最後のプロセスからのものになります。 もちろん、初期化されていない変数にアクセスしようとするなど、スクリプト内でもエラーが発生する可能性があります。

setコマンドとpipefileコマンドを使用すると、このようなエラーが発生したときに何が起こるかを判断できます。 また、パイプチェーンの途中でエラーが発生した場合でもエラーを検出できます。

使い方は次のとおりです。

問題のデモンストレーション

これは簡単なBashスクリプトです。 2行のテキストを端末にエコーします。 テキストをエディタにコピーして「script-1.sh」として保存すると、このスクリプトを実行できます。

 #!/ bin / bash

エコーこれが最初に発生します 
エコーこれは2番目に発生します

実行可能にするには、 chmodを使用する必要があります。

 chmod+xスクリプト-1.sh

コンピューターでスクリプトを実行する場合は、各スクリプトでそのコマンドを実行する必要があります。 スクリプトを実行してみましょう:

 ./script-1.sh 

エラーなしで簡単なスクリプトを実行します。

2行のテキストは、期待どおりにターミナルウィンドウに送信されます。

スクリプトを少し変更してみましょう。 存在しないファイルの詳細をリストするようにlsに依頼します。 これは失敗します。 これを「script-2.sh」として保存し、実行可能にしました。

 #!/ bin / bash

エコーこれが最初に発生します
lsimaginary-ファイル名
エコーこれは2番目に発生します

このスクリプトを実行すると、 lsからのエラーメッセージが表示されます。

 ./script-2.sh 

スクリプトを実行して失敗条件を生成します。

lsコマンドは失敗しましたが、スクリプトは実行を続けました。 また、スクリプトの実行中にエラーが発生した場合でも、スクリプトからシェルへのリターンコードはゼロであり、成功を示します。 エコーと$? シェルに送信された最後の戻りコードを保持する変数。

 エコー$? 

最後に実行されたスクリプトの戻りコードを確認します。

報告されるゼロは、スクリプトの2番目のエコーからの戻りコードです。 したがって、このシナリオには2つの問題があります。 1つ目は、スクリプトにエラーがありましたが、実行を継続しました。 スクリプトの残りの部分が実際に成功した失敗したアクションを予期または依存している場合、それは他の問題につながる可能性があります。 2つ目は、別のスクリプトまたはプロセスがこのスクリプトの成功または失敗をチェックする必要がある場合、誤った読み取り値を取得することです。

set-eオプション

set -e (exit)オプションを指定すると、スクリプトが呼び出すプロセスのいずれかがゼロ以外の戻りコードを生成した場合に、スクリプトが終了します。 ゼロ以外のものはすべて失敗と見なされます。

スクリプトの先頭にset -eオプションを追加することで、その動作を変更できます。 これは「script-3.sh」です。

 #!/ bin / bash 
set -e

エコーこれが最初に発生します
lsimaginary-ファイル名
エコーこれは2番目に発生します

このスクリプトを実行すると、 set -eの効果がわかります。

 ./script-3.sh
 エコー$? 

エラー状態でスクリプトを終了し、戻りコードを正しく設定します。

スクリプトは停止され、シェルに送信される戻りコードはゼロ以外の値です。

パイプの故障への対処

配管により、問題がさらに複雑になります。 パイプされた一連のコマンドから出てくる戻りコードは、チェーンの最後のコマンドからの戻りコードです。 チェーンの途中でコマンドに失敗した場合は、正方形に戻ります。 その戻りコードは失われ、スクリプトは処理を続行します。

組み込みのtrueおよびfalseシェルを使用して、異なる戻りコードでコマンドを配管する効果を確認できます。 これらの2つのコマンドは、それぞれ0または1の戻りコードを生成するだけです。

 真実
エコー$?
 間違い
エコー$? 

bashシェルのtrueおよびfalseの組み込みコマンド。

falsetrueにパイプすると( falseは失敗したプロセスを表します)、 trueの戻りコードはゼロになります。

 false | 真実
エコー$? 

falseをtrueに配管します。

BashにはPIPESTATUSと呼ばれる配列変数があり、これはパイプチェーン内の各プログラムからのすべての戻りコードをキャプチャします。

 false | 真| false | 真実
echo "$ {PIPESTATUS [0]} $ {PIPESTATUS [1]} $ {PIPESTATUS [2]} $ {PIPESTATUS [3]}" 

PIPESTATUSを使用して、パイプチェーン内のすべてのプログラムの戻りコードを確認します。

PIPESTATUSは、次のプログラムが実行されるまでリターンコードのみを保持し、どのリターンコードがどのプログラムに対応するかを判断しようとすると、非常にすぐに混乱する可能性があります。

ここで、 set -o (オプション)とpipefailが使用されます。これは「script-4.sh」です。 これは、存在しないファイルの内容をwcにパイプしようとします。

 #!/ bin / bash 
set -e

エコーこれが最初に発生します
猫のスクリプト-99.sh| wc -l
エコーこれは2番目に発生します

予想通り、これは失敗します。

 ./script-4.sh
 エコー$? 

パイプチェーンでエラーのあるスクリプトを実行する。

最初のゼロはwcからの出力であり、欠落しているファイルの行を読み取らなかったことを示しています。 2番目のゼロは、2番目のechoコマンドからの戻りコードです。

-o pipefailを追加し、「script-5.sh」として保存して、実行可能にします。

 #!/ bin / bash 
set -eo pipefail

エコーこれが最初に発生します
猫のスクリプト-99.sh| wc -l
エコーこれは2番目に発生します

それを実行して、リターンコードを確認しましょう。

 ./script-5.sh
 エコー$? 

パイプチェーンのエラーをトラップし、リターンコードを正しく設定するスクリプトを実行します。

スクリプトが停止し、2番目のechoコマンドは実行されません。 シェルに送信される戻りコードは1つであり、障害を正しく示しています。

関連: LinuxでEchoコマンドを使用する方法

初期化されていない変数のキャッチ

初期化されていない変数は、実際のスクリプトで見つけるのが難しい場合があります。 初期化されていない変数の値をechoし​​ようとすると、 echoは単に空白行を出力します。 エラーメッセージは表示されません。 スクリプトの残りの部分は引き続き実行されます。

これはscript-6.shです。

 #!/ bin / bash 
set -eo pipefail

エコー"$notset" 
echo「別のエコーコマンド」

それを実行し、その動作を観察します。

 ./script-6.sh
 エコー$? 

初期化されていない変数をキャプチャしないスクリプトを実行する。

スクリプトは初期化されていない変数をステップオーバーし、実行を継続します。 戻りコードはゼロです。 非常に長く複雑なスクリプトでこのようなエラーを見つけようとすると、非常に困難になる可能性があります。

Bashで変数を操作する方法
関連するBashで変数を操作する方法

set -u (unset)オプションを使用して、このタイプのエラーをトラップできます。 これをスクリプトの上部にあるセットオプションのコレクションに追加し、「script-7.sh」として保存して実行可能にします。

 #!/ bin / bash 

set -eou pipefail

エコー"$notset" 

echo「別のエコーコマンド」

スクリプトを実行してみましょう:

 ./script-7.sh
 エコー$? 

初期化されていない変数をキャプチャするスクリプトを実行します。

初期化されていない変数が検出され、スクリプトが停止し、戻りコードが1に設定されます。

-u (未設定)オプションは、初期化されていない変数と合法的に対話できる状況によってトリガーされないように十分にインテリジェントです。

「script-8.sh」では、スクリプトは変数New_Varが初期化されているかどうかをチェックします。 スクリプトをここで停止させたくはありません。実際のスクリプトでは、さらに処理を実行して、自分で状況に対処します。

setステートメントの2番目のオプションとして-uオプションを追加したことに注意してください。 -o pipefailオプションは最後に来る必要があります。

 #!/ bin / bash 

set -euo pipefail

if [-z "$ {New_Var:-}"]; それから 

echo"New_Varには値が割り当てられていません。" 

fi

「script-9.sh」では、初期化されていない変数がテストされ、初期化されていない場合は、代わりにデフォルト値が提供されます。

 #!/ bin / bash
set -euo pipefail

default_value = 484
値=${New_Var:-$ default_value}
echo "New_Var = $ Value"

スクリプトは、完了するまで実行できます。

 ./script-8.sh
 ./script-9.sh 

初期化されていない変数が内部で処理され、-uオプションがトリガーされない2つのスクリプトを実行します。

斧で封印

使用するもう1つの便利なオプションは、 set -x (実行および印刷)オプションです。 スクリプトを書いているとき、これは命の恩人になることができます。 実行時にコマンドとそのパラメーターを出力します。

それはあなたに実行トレースの迅速な「大まかな準備ができた」形式を提供します。 ロジックの欠陥の特定とバグの発見がはるかに簡単になります。

set -xオプションを「script-8.sh」に追加し、「script-10.sh」として保存して実行可能にします。

 #!/ bin / bash
set -euxo pipefail

if [-z "$ {New_Var:-}"]; それから
  echo"New_Varには値が割り当てられていません。"
fi

それを実行してトレースラインを確認します。

 ./script-10.sh 

端末に書き込まれた-xトレース行を使用してスクリプトを実行します。

これらの些細なサンプルスクリプトのバグを見つけるのは簡単です。 より複雑なスクリプトを書き始めると、これらのオプションはその価値を証明します。