コマンド置換エラーをキャプチャするには、「-o errtrace」を使用します(例:set -E)。

コマンド置換エラーをキャプチャするには、「-o errtrace」を使用します(例:set -E)。

これによると参考書:

-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()まだ returnSワンパス - 最後に示すように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

関連情報