for ループの条件 continue は、関数のエラー終了を妨げます。

for ループの条件 continue は、関数のエラー終了を妨げます。

AFAICT、continueforループ内で別の関数を呼び出すと、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=1subhshell 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リストの一部ではありません、前にパイプはありません。予約語を使用すると、シェルはすぐに終了する必要があります。

おすすめ記事