AFAICT、continue
forループ内で別の関数を呼び出すと、errexit
意味が損なわれます。main()
関数でエラーが発生した場合は、次の繰り返しを続けたいと思いますbuild()
。
#! /usr/bin/env bash
export PS4='# ${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]}() - [${SHLVL},${BASH_SUBSHELL},$?] '
set -o xtrace
set -o errexit
build() {
local _foo=$1
if [ "${_foo}" -eq 1 ]; then
false
fi
printf "%s with foo=%s builds ok\\n" "${FUNCNAME[0]}" "${_foo}"
}
main() {
for i in 1 2 3; do
build $i || continue
done
}
main "$@"
しかし、ループcontinue
内ではfor
コードが発生します。続ける代わりに、関数内のbuild()
フラグの影響を取り除いてくださいerrexit
。
$ ./foo.sh
# ./foo.sh:5: () - [3,0,0] set -o errexit
# ./foo.sh:23: () - [3,0,0] main
# ./foo.sh:18: main() - [3,0,0] for i in 1 2 3
# ./foo.sh:19: main() - [3,0,0] build 1
# ./foo.sh:8: build() - [3,0,0] local _foo=1
# ./foo.sh:10: build() - [3,0,0] '[' 1 -eq 1 ']'
# ./foo.sh:11: build() - [3,0,0] false
# ./foo.sh:14: build() - [3,0,1] printf '%s with foo=%s builds ok\n' build 1
build with foo=1 builds ok
# ./foo.sh:18: main() - [3,0,0] for i in 1 2 3
# ./foo.sh:19: main() - [3,0,0] build 2
# ./foo.sh:8: build() - [3,0,0] local _foo=2
# ./foo.sh:10: build() - [3,0,0] '[' 2 -eq 1 ']'
# ./foo.sh:14: build() - [3,0,0] printf '%s with foo=%s builds ok\n' build 2
build with foo=2 builds ok
# ./foo.sh:18: main() - [3,0,0] for i in 1 2 3
# ./foo.sh:19: main() - [3,0,0] build 3
# ./foo.sh:8: build() - [3,0,0] local _foo=3
# ./foo.sh:10: build() - [3,0,0] '[' 3 -eq 1 ']'
# ./foo.sh:14: build() - [3,0,0] printf '%s with foo=%s builds ok\n' build 3
build with foo=3 builds ok
printf
前の行の終了コードがある行に見られるように、false
これは実際には1
(前の角かっこ内の3番目の数字)であるため、所定の位置errexit
にないように実行されます。
# ./foo.sh:14: build() - [3,0,1] printf '%s with foo=%s builds ok\n' build 1
|| continue
削除すると、シェルが終了してi=1
subhshell errexit
/関数に渡されることを確認しました。
どんな助けでも大変感謝します。
バージョン
~ $ bash --version
GNU bash, version 5.0.3(1)-release (x86_64-pc-linux-gnu)
修正する
次の質問に対する良い答えがたくさんありますなぜこれは。問題を解決する方法については、このソリューションが私が望むタスクを実行するようにスクリプトを取得する最も簡単な方法であることがわかりました。スクリプトを次に変更してくださいfalse
。
false || return $?
もちろん、欠点は、関数が呼び出すすべてのコマンドに対してこれを行う必要があることです。run()
渡されたコマンドを実行して戻りコードを確認したら、それに応じてスクリプトを失敗させるラッパーを使用する以前の方法に戻る必要があります。私はerrexit
あなたがすることが期待されることをやると思います:-)
答え1
-e
これは/-errexit
の説明と一致しているようです。バッシュ文書:
失敗したコマンドがwhileまたはUntilキーワード(ifステートメントのテスト部分)の直後にあるコマンドリストの一部である場合、シェルは終了しません。&&または||内で実行されるコマンドの一部のリスト最後の&&または||後の最後のコマンドを除く、パイプライン内のすべてのコマンドまたはコマンドの戻り状態は!に反転されます。
[...]-eを省略したコンテキストで複合コマンドまたはシェル関数が実行されている場合、-eが設定されてコマンドがエラーを返しても、複合コマンドまたは関数の本文内で実行されるすべてのコマンドは-e設定の影響を受けません。状態。
これはすでにこの電卓の問題、次へ接続このメールそして、次のテキストが付属しています。
> My initial gripe about errexit (and its man page description) is that the
> following doesn't behave as a newbie would expect it to:
>
> set -e
> f() {
> false
> echo "NO!!"
> }
> f || { echo "f failed" >&2; exit 1; }
Indeed, the correct behavior mandated by POSIX (namely, that 'set -e' is
completely ignored for the duration of the entire body of f(), because f
was invoked in a context that ignores 'set -e') is not intuitive. But
it is standardized, so we have to live with it.
これPOSIX説明は-e
次のとおりです。
-e
このオプションがオンの場合、シェルエラーの結果にリストされている理由で単純なコマンドが失敗するか、終了ステータス値> 0を返し、while、Until、またはifキーワードの後にある複合リストの一部ではない場合、ANDまたはORリストの一部ではありません、前にパイプはありません。予約語を使用すると、シェルはすぐに終了する必要があります。
答え2
~から手動[強調表示]:
errexit
と同じです
-e
。
-e
パイプライン[...](単一の単純コマンド[...]、リスト[...]、または複合コマンド[...]で構成できます)がゼロ以外の状態を返す場合は、すぐに終了してください。失敗したコマンドが
&&
[...]の一部であるか、リストから実行されたコマンド(||
最後または次のコマンドを除く)の場合、シェルは終了しません。&&
||
、[… ][… ]
[...] シェル関数が無視されたコンテキストで実行される場合、[
-e
...] 関数本体内で実行されるすべてのコマンドは、この設定が設定され、コマンドが失敗状態を返しても-e
この設定の影響を受けません。-e
。[… ]
シェル機能は無視されたbuild $i || continue
build
コンテキストで実行されます。関数本体内で実行されるコマンドなので、設定の影響を受けないため、実行を妨げることもありません。-e
false
-e
printf
削除し|| continue
て呼び出すと、build $i
関数の各部分が次の-e
ようなコンテキストに配置されます。いいえfalse
は無視されるので、それ以降はコード全体がfalse
到着せずに終了しますprintf
。
これはerrexit
(無視しない場合)スクリプト全体を終了するグローバル設定のようです。関数を終了させることはできませんが(または少なくとも簡単にはできません)、スクリプト全体を終了することはできません。