これによると参考書:
-E(-o errtrace とも呼ばれる)
設定されている場合、ERR のすべてのトラップは、シェル関数、コマンド置換、およびサブシェル環境で実行されるコマンドによって継承されます。この場合、ERRトラップは通常継承されません。
しかし、次のことがうまくいかなかったので、間違って解釈したようです。
#!/usr/bin/env bash
# -*- bash -*-
set -e -o pipefail -o errtrace -o functrace
function boom {
echo "err status: $?"
exit $?
}
trap boom ERR
echo $( made up name )
echo " ! should not be reached ! "
私はすでに単純な割り当てがmy_var=$(made_up_name)
スクリプトを終了していることを知っていますset -e
(例:errexit)。
-E/-o errtrace
上記のコードのように動作する必要がありますか?それとも私が間違って読んだのでしょうか?
答え1
注:zsh
ここにあるほとんどの例では、「インライン注釈」を許可するように設定せずに行ったように実行しないでくださいsh <<-\CMD
。
さて、上記のコメントで述べたように、私は具体的には知りません。バッシュset -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
Sワンパス - 最後に示すように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
上記の例は、POSIXパラメータ置換の4つの形式とそのさまざまな形式の両方を利用しています。:colon null
または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)
)は、現在のシェルコマンドの実行よりもシェル評価プロセスで早く発生します。この時点でエラーが発生する可能性があります。
ジョブで発生する問題は、現在のシェルがエラーと評価されたときに、$(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
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
答え2
これはbashのバグによるものです。コマンド置換フェーズで
echo $( made up name )
made
サブシェルで実行されますが(または見つかりません)、サブシェルは「最適化」されており、親シェルでは特定のトラップを使用しません。これは固定されています。バージョン 4.4.5:
場合によっては、単純なコマンドがブランチを削除するように最適化され、EXITトラップは実行されません。
Bash 4.4.5以降を使用すると、次の出力が表示されます。
error.sh: line 13: made: command not found
err status: 127
! should not be reached !
トラップハンドラが期待どおりに呼び出され、サブシェルが終了します。 (set -e
これにより、親シェルではなくサブシェルのみが終了するため、「到達しないでください」というメッセージが実際に到達する必要があります。)
以前のバージョンの回避策は、最適化されていないサブシェル全体を強制的に生成することでした。
echo $( ( made up name ) )
追加スペースは必須算術拡張と区別してください。
答え3
設定されている場合、ERR のすべてのトラップは、シェル関数、コマンド置換、およびサブシェル環境で実行されるコマンドによって継承されます。
スクリプトは実行(echo $( made up name )
)コマンドです。 Bash コマンドでは、次のいずれかで区切ります。;または新しいチーム。指揮中
echo $( made up name )
$( made up name )
注文の一部と見なされます。この部分が失敗してエラーを返しても、echo
それがわからないため、コマンド全体が成功します。コマンドはゼロを返すので、トラップはトリガされません。
割り当てとエコーは2つのコマンドに入れる必要があります。
var=$(made_up_name)
echo $var