ほとんどのLinuxガイドは、「実行する必要があるcommand_1
、次にcommand_2
、次にcommand_3
"など。すべてを手動で実行して時間を無駄にしたくないので、スクリプトを作成します
command_1
command_2
command_3
一度実行します。しかし、多くの場合、一部のコマンドは失敗し、どのコマンドが失敗したのかはわかりません。また、通常、何かが以前に失敗した場合、残りのコマンドはすべて意味がありません。したがって、より良いスクリプトは次のようなものになります
(command_1 && echo OK command_1 || (echo FAILED command_1; false) )
&& (command_2 && echo OK command_2 || (echo FAILED command_2; false) )
&& (command_3 && echo OK command_3 || (echo FAILED command_3; false) )
&& echo DONE
|| echo FAILED
ただし、ボイラープレートコードを多く記述し、各コマンドを3回繰り返す必要があります。また、中括弧の一部を誤入力する可能性が高すぎます。最後のスクリプトが実行するより便利な方法はありますか?特に:
ソリューションには2つのタイプがあります。
try
ステートメントを追加して、チェーンされたコマンドを個別に分割することにより、コマンドをコピーして貼り付けた後、コマンドを変更する必要があります。もの(ジェイセンによる答え)。あなたは人々を揺るがしますが、私はこの質問をしばらく開いたままにしておきます。たぶん、両方のニーズを満たすソリューションを知っている人がいるかもしれません(最後の行に失敗したコマンドを出力し、変更せずにコマンドをコピーして貼り付けることができます)。
1つのオプションは、コマンドをbashスクリプトに入れ、set -e
で開始することです。
これにより、コマンドがゼロ以外の終了ステータスで終了した場合、スクリプトが早期に終了します。
スタックオーバーフローに関するこの質問もご覧ください: https://stackoverflow.com/q/19622198/82819
エラーを出力するには、次を使用できます
trap 'do_something' ERR
ここで、do_something
は、エラーを表示するために作成するコマンドです。
これがどのように機能するかを確認するスクリプトの例です。
#!/bin/bash
set -e
trap 'echo "******* FAILED *******" 1>&2' ERR
echo 'Command that succeeds' # this command works
ls non_existent_file # this should fail
echo 'Unreachable command' # and this is never called
# due to set -e
そしてこれは出力です:
$ ./test.sh
Command that succeeds
ls: cannot access 'non_existent_file': No such file or directory
******* FAILED *******
また、@ jickで述べたように、パイプラインの終了ステータスはデフォルトでfinalコマンド。これは、パイプライン内の非最終コマンドが失敗した場合、set -e
によってキャッチされないことを意味します。この問題に関心がある場合は、set -o pipefail
を使用して修正できます。
提案されたように、関数を使用して、私の@ glenn jackmanおよび@ Monty Harderネストされた引用を回避するため、ハンドラーはスクリプトを読みやすくすることができます。とにかく関数を使用しているので、set -e
を完全に削除し、ハンドラーでexit 1
を使用しました。
#!/bin/bash
error_handler() {
echo "******* FAILED *******" 1>&2
exit 1
}
trap error_handler ERR
echo 'Command that succeeds' # this command works
ls non_existent_file # this should fail
echo 'Unreachable command' # and this is never called
# due to the exit in the handler
スクリプトの終了ステータスは異なりますが、出力は上記と同じです。
異常な解決策が必要ですか? make
をインストールしている場合は、コマンドのリストをMakefile
に入れてmake
を実行できます。追加された利点:エラーが発生したかどうかを確認する必要がありません。 make
は、各コマンドの戻り値を自動的にチェックします。ゼロ以外の場合、レシピはエラーで終了します。特定のコマンドのエラーを無視したい場合は、|| true
。
Makefileの例:
.PHONY: all
.SILENT:
all:
echo "Started list of commands."
true
echo "Executing a command which will fail, but I want to ignore failure."
false || true
echo "Executing a command which will definitely fail."
false
echo "This code will not be reached."
注:上記のようにコマンドをインデントする(heh)ことを確認してください。
これはbashでのみ機能すると思いますが、以下を試すことができます。
set -o xtrace
set -o errexit
または、簡潔になりたい場合は、
set -ex
これは2つのことを行います:errexit
(-e)はエラーが発生したときにスクリプトを中止し、xtrace
(-x)はbashが実行する直前に各コマンドを出力します。失敗した場合は、それが何を実行していたかを正確に把握できます。
1つの欠点は、出力が乱雑になることですが、それで問題がなければ、これは最小限の作業でかなり良いソリューションになります。
set -o pipefail
も含めることをお勧めします。それ以外の場合、foo | bar
を実行すると、foo
の失敗は通知なく無視されます。 (警告:パイプステートメントの「終了コード」を微妙に変更するため、他の誰かのスクリプトを変更する場合は注意して使用してください。また、foo
の失敗に実際には気にしない場合があるので、明らかにそのような場合、pipefail
は使用できません。)あなたはこのようなことをすることができます:
$ for f in command_1 command_2 command_3 command_4
do
$f
rc=$?
if [[ 0 != $rc ]]; then echo Failed command: ${f}; break; fi
done
これは、コマンドにオプションがないことを前提としています。コマンドがある場合は、各コマンド/オプションセットを引用符で囲む必要があります。
failed=0
try(){
(( failed )) && return
"$@"
ec=$?
if (( ec ))
then
failed=$ec
echo "failed($failed): $*" >&2
fi
}
try something args
try other-thing more args
if (( failed ))
then
echo "something went wrong: see above." >&2
exit 1
fi
対話型シェルで実行する場合は、このソリューションを使用します。
set -x; command_1 && command_2 && command_3 && echo "Everything OK" || echo "Error while executing last command"; set +x
すべてのコマンドが成功した場合の出力:
+ command_1
output of command 1
+ command_2
output of command 2
+ command_3
output of command 3
+ echo "Everything OK"
Everything OK
+ set +x
command_2
が失敗した場合の出力:
+ command_1
output of command 1
+ command_2
output of command 2
+ echo "Error while executing last command"
+ set +x
set -x
コマンドは、実行されたすべてのコマンドのロギングを有効にします。ログに記録されたコマンドには、1つ以上のプラス記号が付加されます(スタイルは、変数$PS4
を使用して変更できます)。 set +x
はこのモードを無効にします。
set -x
はall実行されたコマンドのロギングを有効にするため、set +x
とset -x
を同じ行に配置する必要があります— $COMMAND_Prompt
およびそれによって実行されたコマンドを含みます。
シェルスクリプトにも使用できます。
#!/bin/bash -ex
command_1
command_2
command_3
シェルオプションは、Shebang行に埋め込むことができます。 -e
は、最初に失敗したコマンドの後にシェルを終了させます(すべての行が&&
で終わる場合など)。 -x
は、コマンドのロギングを有効にします。インタプリタに渡すことができるのはone引数のみであることに注意してください。 /usr/bin/env
を使用して複数の引数を渡します(#!/usr/bin/env -S bash -ex arg1 arg2 argN
)。
たぶん、あなたがすでにやっていることのための単なる関数:
function run_cmd
( $* && echo OK $* || ( echo FAILED $*; false ) )
run_cmd echo 1 &&
run_cmd false 2 &&
run_cmd echo 3 &&
echo DONE || echo FAILED
これの出力は:
1
OK echo 1
FAILED false 2
FAILED