LinuxのBashスクリプトでsetとpipefailを使用する方法
公開: 2022-06-25 Linuxのset
コマンドとpipefail
コマンドは、Bashスクリプトで障害が発生したときに何が起こるかを指示します。 やめるべきか、継続すべきかということ以上に、考えることがたくさんあります。
関連:シェルスクリプトの初心者向けガイド:基本
Bashスクリプトとエラー条件
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の戻りコードを生成するだけです。
真実
エコー$?
間違い
エコー$?
false
をtrue
にパイプすると( false
は失敗したプロセスを表します)、 true
の戻りコードはゼロになります。
false | 真実
エコー$?
BashにはPIPESTATUS
と呼ばれる配列変数があり、これはパイプチェーン内の各プログラムからのすべての戻りコードをキャプチャします。
false | 真| false | 真実
echo "$ {PIPESTATUS [0]} $ {PIPESTATUS [1]} $ {PIPESTATUS [2]} $ {PIPESTATUS [3]}"
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
エコー$?
スクリプトは初期化されていない変数をステップオーバーし、実行を継続します。 戻りコードはゼロです。 非常に長く複雑なスクリプトでこのようなエラーを見つけようとすると、非常に困難になる可能性があります。
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
斧で封印
使用するもう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
これらの些細なサンプルスクリプトのバグを見つけるのは簡単です。 より複雑なスクリプトを書き始めると、これらのオプションはその価値を証明します。