注:zsh
ここでの例のほとんどで「インラインコメント」を受け入れるように設定しないと、「悪意のあるパターン」について文句を言い、プロキシシェルを介して実行しませんsh <<-\CMD
。
さて、上記のコメントで述べたように、私はbashset -E
について具体的には知りませんが、POSIX互換シェルは、必要に応じて値をテストする簡単な手段を提供することを知っています:
sh -evx <<-\CMD
_test() { echo $( ${empty:?error string} ) &&\
echo "echo still works"
}
_test && echo "_test doesnt fail"
# END
CMD
sh: line 1: empty: error string
+ echo
+ echo 'echo still works'
echo still works
+ echo '_test doesnt fail'
_test doesnt fail
あなたは、私が使用してもいることがわかります上記のparameter expansion
テストに${empty?} _test()
まだ return
ね、最後にevincedされるよう-パスecho
これが失敗した値は殺すために発生$( command substitution )
-それが含まれているサブシェルを、その親シェル_test
、この時点で-トラック輸送を続けています。そしてecho
気にしない-それはテストで\newline; echo
はないだけを提供することは十分に幸せです。
しかし、これを考慮してください:
sh -evx <<-\CMD
_test() { echo $( ${empty:?error string} ) &&\
echo "echo still works" ; } 2<<-INIT
${empty?function doesnt run}
INIT
_test ||\
echo "this doesnt even print"
# END
CMD
_test+ sh: line 1: empty: function doesnt run
今_test()'s
では事前に評価されたパラメーターを入力に入力したためINIT here-document
、_test()
関数はまったく実行しようとさえしません。さらに、sh
シェルは明らかにゴーストを完全に放棄し、echo "this doesnt even print"
印刷さえしません。
おそらくそれはあなたが望むものではありません。
これは、${var?}
スタイルパラメータ拡張が、パラメータが欠落した場合に終了するように設計されてshell
いるために発生します。これは次のように機能します。
${parameter:?[word]}
場合、エラーを示すNull
か、Unset.
パラメータが未設定またはヌルの場合、expansion of word
でなければならない(または単語が省略された場合、それを示すメッセージが設定されていない)written to standard error
とshell exits with a non-zero exit status
。それ以外の場合、の値parameter shall be substituted
。対話型シェルを終了する必要はありません。
ドキュメント全体をコピー/貼り付けすることはしませんが、set but null
値に失敗したい場合は、次の形式を使用します。
${var
:? error message }
:colon
上記のように。必要に応じてnull
値が成功するために、ちょうどコロンを省略します。すぐに表示するように、設定値に対してのみ無効にして失敗することもできます。
別の実行 _test():
sh <<-\CMD
_test() { echo $( ${empty:?error string} ) &&\
echo "echo still works" ; } 2<<-INIT
${empty?function doesnt run}
INIT
echo "this runs" |\
( _test ; echo "this doesnt" ) ||\
echo "now it prints"
# END
CMD
this runs
sh: line 1: empty: function doesnt run
now it prints
これはあらゆる種類のクイックテストで機能しますが、上記のよう_test()
に、pipeline
失敗の真ん中から実行され、実際、そのcommand list
サブシェルを含むサブシェルは完全に失敗します。関数内のコマンドも実行もecho
、現在は印刷されているため、簡単にテストできることも示されていますecho "now it prints"
。
悪魔は細部に宿っていると思います。上記の場合、終了するシェルはスクリプトではなく_main | logic | pipeline
、( subshell in which we ${test?} ) ||
これほど少ないのサンドボックスが要求されます。
そして、それは明らかではないかもしれませんが、反対の場合のみ、またはset=
値のみを渡したい場合、それもかなり簡単です:
sh <<-\CMD
N= #N is NULL
_test=$N #_test is also NULL and
v="something you would rather do without"
( #this subshell dies
echo "v is ${v+set}: and its value is ${v:+not NULL}"
echo "So this ${_test:-"\$_test:="} will equal ${_test:="$v"}"
${_test:+${N:?so you test for it with a little nesting}}
echo "sure wish we could do some other things"
)
( #this subshell does some other things
unset v #to ensure it is definitely unset
echo "But here v is ${v-unset}: ${v:+you certainly wont see this}"
echo "So this ${_test:-"\$_test:="} will equal NULL ${_test:="$v"}"
${_test:+${N:?is never substituted}}
echo "so now we can do some other things"
)
#and even though we set _test and unset v in the subshell
echo "_test is still ${_test:-"NULL"} and ${v:+"v is still $v"}"
# END
CMD
v is set: and its value is not NULL
So this $_test:= will equal something you would rather do without
sh: line 7: N: so you test for it with a little nesting
But here v is unset:
So this $_test:= will equal NULL
so now we can do some other things
_test is still NULL and v is still something you would rather do without
上記の例では、4つの形式のPOSIXパラメーター置換とそれらのさまざまな:colon null
or not null
テストを利用しています。上記のリンクに詳細情報がありますが、ここにもあります。
そして、_test
関数の動作も示すべきだと思いますか?empty=something
関数のパラメーターとして(または事前にいつでも)宣言するだけです。
sh <<-\CMD
_test() { echo $( echo ${empty:?error string} ) &&\
echo "echo still works" ; } 2<<-INIT
${empty?tested as a pass before function runs}
INIT
echo "this runs" >&2 |\
( empty=not_empty _test ; echo "yay! I print now!" ) ||\
echo "suspiciously quiet"
# END
CMD
this runs
not_empty
echo still works
yay! I print now!
この評価は独立していることに注意する必要があります。失敗するために追加のテストは必要ありません。さらにいくつかの例:
sh <<-\CMD
empty=
${empty?null, no colon, no failure}
unset empty
echo "${empty?this is stderr} this is not"
# END
CMD
sh: line 3: empty: this is stderr
sh <<-\CMD
_input_fn() { set -- "$@" #redundant
echo ${*?WHERES MY DATA?}
#echo is not necessary though
shift #sure hope we have more than $1 parameter
: ${*?WHERES MY DATA?} #: do nothing, gracefully
}
_input_fn heres some stuff
_input_fn one #here
# shell dies - third try doesnt run
_input_fn you there?
# END
CMD
heres some stuff
one
sh: line :5 *: WHERES MY DATA?
そして最後に、元の質問に戻ります。サブシェルでエラーを処理する方法は$(command substitution)
?真実は-2つの方法がありますが、どちらも直接的なものではありません。問題の核心はシェルの評価プロセスです-シェル拡張($(command substitution)
)は、現在のシェルコマンドの実行よりもシェルの評価プロセスの早い段階で発生します。
opが経験する問題は、現在のシェルがエラーを評価するまでに、$(command substitution)
サブシェルがすでに置き換えられていることです。エラーは残っていません。
では、2つの方法は何ですか?どちらかで明示的に行う$(command substitution)
サブシェル、テストなしでテストするか、その結果を現在のシェル変数に吸収してその値をテストします。
方法1:
echo "$(madeup && echo \: || echo '${fail:?die}')" |\
. /dev/stdin
sh: command not found: madeup
/dev/stdin:1: fail: die
echo $?
126
方法2:
var="$(madeup)" ; echo "${var:?die} still not stderr"
sh: command not found: madeup
sh: var: die
echo $?
1
これは、行ごとに宣言された変数の数に関係なく失敗します。
v1="$(madeup)" v2="$(ls)" ; echo "${v1:?}" "${v2:?}"
sh: command not found: madeup
sh: v1: parameter not set
そして、戻り値は一定のままです。
echo $?
1
今トラップ:
trap 'printf %s\\n trap resurrects shell!' ERR
v1="$(madeup)" v2="$(printf %s\\n shown after trap)"
echo "${v1:?#1 - still stderr}" "${v2:?invisible}"
sh: command not found: madeup
sh: v1: #1 - still stderr
trap
resurrects
shell!
shown
after
trap
echo $?
0
echo $( made up name )
と$( made up name )
、目的の動作が生成されます。私には説明がありません。