以下は、私が書いているスクリプトの簡略化されたスキームです。プログラムはさまざまな方法でパラメーターを取得する必要があるため、いくつかの関数に細かく分割されています。
問題は、より深い関数からの戻り値のチェーンロードが、メッセージを表示するために結果をチェックするトラップで壊れることです。
#! /usr/bin/env bash
check_a_param() {
[ "$1" = return_ok ] && return 0 || return 3
}
check_params() {
# This trap should catch negative results from the functions
# performing actual checks, like check_a_param() below.
return_trap() {
local retval=$?
[ $retval -ne 0 ] && echo 'Bad, bad… Dropping to manual setup.'
return $retval
}
# check_params can be called from different functions, not only
# setup(). But the other functions don’t care about the return value
# of check_params().
[ "${FUNCNAME[1]}" = setup ] \
&& trap "return_trap; got_retval=$?; trap - RETURN; return $got_retval;" RETURN
check_a_param 'return_bad' || return $?
# …
# Here we check another parameters in the same way.
# …
echo 'Provided parameters are valid.'
return 0 # To be sure.
}
ask_for_params() {
echo 'User sets params manually step by step.'
}
setup() {
[ "$1" = force_manual ] && local MANUAL=t
# If gathered parameters do not pass check_params()
# the script shall resort to asking user for entering them.
[ ! -v MANUAL ] && {
check_params \
&& echo "check_params() returned with 0. Not running manual setup."
|| false
}|| ask_for_params
# do_the_job
}
setup "$@" # Either empty or ‘force_manual’.
どのように動作するか:
↗ 3 → 3→ trap →3 ↗ || ask_for_params ↘
check_a_param >>> check_params >>> [ ! -v MANUAL ] ↓
↘ 0 → 0→ trap →0 ↘ && ____________ do_the_job
この考え方は、チェックが失敗した場合、そのリターン コードも強制的に返され、それが今度は の条件check_params()をトリガーするというものです。しかし、トラップは 0 を返します。|| ask_for_paramssetup()
↗ 3 → 3→ trap →0
check_a_param >>> check_params >>> [ ! -v MANUAL ] &&… >>> do_the_job
↘ 0 → 0→ trap →0
スクリプトをそのまま実行しようとすると、次のように表示されます。
Bad, bad… Dropping to manual setup.
check_params() returned with 0. Not running manual setup.
これは、悪い結果がトラップ(!) を引き起こしたが、それを設定した親関数が結果を渡さなかったことを意味します。
私が試したハックを設定しようとして
- retval をグローバル変数として
declare -g retval=$?設定return_trap()し、トラップを設定する行でその値を使用します。変数は設定されています ([ -v retval ]正常に返されます) が、値がありません。面白い。 - 、 の外側に置き
retval=Eeh、通常のパラメーターとして設定します。いいえ、関数内はグローバル変数の値を設定せず、「Eeh」のままです。いいえ、指示はありません。デフォルトではグローバルとして扱われるべきです。にとを入れて、 の最後に を付けて印刷すると、3 が表示されるはずです。少なくとも私はそうします。予想通り、ここでは何の違いもありません。check_params()return_trap()$?retvallocaltest=1check_params()test=3check_a_param()echo $testsetup()declare -g - 多分それは機能の範囲ですか?いいえ、それもありません。
return_trap()一緒に移動しdeclare -g retval=Eehても違いはありません。 最新のソフトウェアが衰退を意味するとき、古き良きファイルへの書き込みに頼る時が来ました。
retval=$?; echo $retval >/tmp/tinを使用して retval を /tmp/t に出力し、return_trap()それを読み取ります。trap "return_trap; trap - RETURN; return $(</tmp/t)" RETURN
ここで、ファイルから数値を読み取る最後の return ディレクティブが実際には 3 を返すことが最終的にわかります。しかし、check_params()それでも 0 を返します!
++ trap - RETURN
++ return 3
+ retval2=0
+ echo 'check_params() returned with 0. Not running manual setup.'
check_params() returned with 0. Not running manual setup.
コマンドの引数trapが厳密に関数名である場合、元の結果が返されます。元のものであり、return_trap()返されるものではありません。結果をインクリメントしようとしましたが、それでも 3 になりました。「なぜそんなにトラップを設定解除する必要があるのですか?」という質問もあるかもしれません。check_params()別の関数から呼び出された場合でも、毎回トラップがトリガーされる別のバグを回避するためです。RETURN のトラップはローカルなものであり、デバッグまたはトレース フラグが明示的に設定されていない限り、別の関数に継承されませんが、実行間でトラップが設定されているように見えます。または、bash はそれらのトラップを保持します。このトラップは、特定の関数から check_params() が呼び出された場合にのみ設定する必要がありますが、トラップが設定解除されていない場合は、毎回トリガーされ続けますcheck_a_param()の内容に関係なく、ゼロより大きい値を返しますFUNCNAME[1]。
ここで私はあきらめます。なぜなら、私が今見ている唯一の出口は、各|| return $?inの前に呼び出し関数のチェックを実装することだからcheck_params()です。でも目が痛くなるくらい醜い。
私はそれを追加するだけかもしれません.trapを設定$?する行では、常に0を返します.localretvalreturn_trap()
trap "return_trap; [ -v retval ]; echo $?; trap - RETURN; return $retval" RETURN
retval実際に設定されているかどうかに関係なく 0が出力されますが、
trap "return_trap; [ -v retval ] && echo set || echo unset; trap - RETURN; return $retval" RETURN
「未設定」と出力されます。
GNU bash、バージョン 4.3.39(1) リリース (x86_64-pc-linux-gnu)