set -e
の動作をエミュレートする方法を見つけることを試みていますが、その関数のスコープ内でのみです。Bash:エラーでの優雅な関数の破棄
基本的には、単純なコマンドでトリガーすると、set -e
が1つ上のレベルを返す関数が必要です。目標は、危険な仕事の集合を機能に分けて、それらを適切に処理できるようにすることです。
set -e
の動作をエミュレートする方法を見つけることを試みていますが、その関数のスコープ内でのみです。Bash:エラーでの優雅な関数の破棄
基本的には、単純なコマンドでトリガーすると、set -e
が1つ上のレベルを返す関数が必要です。目標は、危険な仕事の集合を機能に分けて、それらを適切に処理できるようにすることです。
コマンドを失敗させて1を返す場合は、各コマンドの後に|| return 1
と入力してください。例えば
:
false || return 1 # This will always return 1
私は明示的な取り扱いなしで失敗する任意のコマンドをさせることはないの大ファンです。私のスクリプトでは、戻りコードではない方法でエラーを返し、すべてのエラーをトラップする例外処理テクニックを使用しています(bashトラップで)。非ゼロの戻りコードを持つコマンドは、自動的に状況やバグが不適切に処理されたことを意味します。このような状況が発生するとすぐにスクリプトが失敗することを好みます。
注意:この手法の使用を強くお勧めします。あなたがサブシェル環境でこの関数を実行すると、あなたはほとんどあなたが望む動作を得るでしょう。以下を検討してください:
#!/bin/bash
foo() ( # Use parens to get a sub-shell
set -e # Does not impact the main script
echo This is executed
false
echo This should *not* be executed
)
foo # Function call fails, returns 1
echo return: $?
# BUT: this is a good reason to avoid this technique
if foo; then # Set -e is invalid in the function
echo Foo returned 0!!
else
echo fail
fi
false # Demonstrates that set -e is not set for the script
echo ok
あなたは何をお勧めしますか? bashスクリプトのエラー処理は、体系的に(どこでも、何らかのフレームワークで、アドホックなアプローチではなく)実行するのに非常に苦労したことです。他の人たちがどうやってそれを行うのか不思議です。私はそれがどこでも深く議論されて見たことがありません。 – Fred
'set -e'は例外処理に最も近いものですが、それは非常に原始的です。関数の中でクリーンアップを扱い、明示的に何らかの失敗(例えば 'cmd || return')を返すトラップを返すことができます。これはおそらくあなたが得ることができる最高のものです。 –
bashサポートに関しては、yes set -eとtrapがほとんどありますが、エラー処理のサポートでは、処理されていないすべてのゼロ以外の戻りコードによってスクリプトが中止される例外処理メカニズムを構築できます。これは私が構築したものであり、長年のコードの信頼性という大きなメリットにもかかわらず、他の誰かがそのことを言及したことは一度もありませんでした。 – Fred
Javaが提供するものとやや同じように「ネストされた例外」を探しているようです。スコープを設定する必要がある場合は、機能の冒頭でset -e
を実行し、set +e
を実行してから戻ってください。
効率的または便利ではないもう一つのアイデアは、サブシェルであなたの関数を呼び出すことです:いずれの場合で
# some code
(set -e; my_function)
if [[ $? -ne 0 ]]; then
# the function didn't succeed...
fi
# more code
、set -e
はでエラーを処理しない最大の方法であることに注意してください。シェルスクリプト。あまりにも多くの問題が非常に信頼性の低いものになっています。これらの関連記事を参照してください。
私は本番環境で長い時間のために存在する必要がある大規模なスクリプトのために取るアプローチは次のとおりです。
mv
,cp
,mkdir
,ln
、rm
など) -:)慎重に引数を検証しても、例外時に例外
# library of common functions
trap '_error_handler' ERR
trap '_exit_handler' EXIT
trap '_int_handler' SIGINT
_error_handler() {
# appropriate code
}
# other handlers go here...
#
exit_if_error() {
error_code=${1:-0}
error_message=${2:-"Uknown error"}
[[ $error_code == 0 ]] && return 0 # it is all good
# this can be enhanced to print out the "stack trace"
>&2 printf "%s\n" $error_message
# out of here
my_exit $error_code
}
my_exit() {
exit_code=${1:-0}
_global_graceful_exit=1 # this can be checked by the "EXIT" trap handler
exit $exit_code
}
# simple wrapper for cp
my_cp() {
# add code to check arguments more effectively
cp $1 $2
exit_if_error $? "cp of '$1' to '$2' failed"
}
# main code
source /path/to/library.sh
...
my_cp file1 file2
# clutter-free code
これは、ERR
とEXIT
イベントに行動を起こすtrap
の有効活用とともに、信頼性の高いシェルスクリプトを書くための良い方法だろう。
'set -e'は私の望むことに対して実際には全く機能しません。関数内に設定されていても、スクリプト全体が即座に中止されます。私はそのスクリプトを実行する前にそれを設定することと変わりありません。 http://pastebin.com/p84B5R8b – OstermanA
ちょうど私の答えのサブシェルのアイデアを見てみましょう。確実に実行する必要があるものを書く場合は、 'set -e'はお勧めしません。 – codeforester
あなたのexit_if_error関数は、私が望んでいたものではありません...しかし、それが意味をなさなくするほど、私はもっと見ています。本当の試行錯誤の仕組みがなければ、おそらく私ができることは最高です。 – OstermanA
もっと研究して、私はGoogleのShell Style Guideのような解決策を見つけました。ここではいくつか真剣におもしろい提案がありますが、私は可読性のためにこれを使用するつもりです:
if ! mv "${file_list}" "${dest_dir}/" ; then
echo "Unable to move ${file_list} to ${dest_dir}" >&2
exit "${E_BAD_MOVE}"
fi
エコーの必要はありません。 'mv'はすでに素晴らしいエラーメッセージを出しています。 –
合意。これは簡単な例です。はるかに有用なメッセージは、その機能が何であるか、なぜ失敗が致命的であるか、などです。 – OstermanA
しかし、それはどちらもうまくいかない。これは私がエラーチェックを行う方法ではありません、私は最初にチェックしておくべきです。私の反応の一部を削除しました。 – Fred
場合によっては、トラップを閉じ込める方がよい場合もあります。 –
「トラップをトラップする」とはどういう意味ですか? – Fred