ループから外れると、ゼロ以外の終了コードがトリガされます。

ループから外れると、ゼロ以外の終了コードがトリガされます。

一連のジョブを実行し、そのうちの1つでも失敗した場合は停止したかったので、次のように書きました。

for task in [TASKS]; do
  process "$task" || break
  commit "$task"
done

これはうまくいきますが(例:指定済み) 早く中断してもループの終了状態はゼロです。理想的には、break-ingは失敗を伝えることができます。

0リターンが文書化された動作であることを知っていますが、break比較的きれいな回避策があるかどうか疑問に思います。私が想像できる最善の方法は、それを関数で囲んでdidBreak変数を設定し、それを(関数の)終了状態として使用することです。うまくいきますが、複雑すぎると感じます。

答え1

これは多くのシェルで使用できます! break(pdkshベースのシェルとshFreeBSDシェルを除く(渡す デザイン)私のテストから):

$ zsh -c 'for i in x; do ! break; echo "$i"; done'; echo "$?"
1
$ bash -c 'for i in x; do ! break; echo "$i"; done'; echo "$?"
1
$ ksh88 -c 'for i in x; do ! break; echo "$i"; done'; echo "$?"
1
$ ksh93 -c 'for i in x; do ! break; echo "$i"; done'; echo "$?"
1
$ dash -c 'for i in x; do ! break; echo "$i"; done'; echo "$?"
1
$ yash -c 'for i in x; do ! break; echo "$i"; done'; echo "$?"
1
$ bosh -c 'for i in x; do ! break; echo "$i"; done'; echo "$?"
1
$ pdksh -c 'for i in x; do ! break; echo "$i"; done'; echo "$?"
0
$ mksh -c 'for i in x; do ! break; echo "$i"; done'; echo "$?"
0
$ posh -c 'for i in x; do ! break; echo "$i"; done'; echo "$?"
0

errexitどちらも実行されません。

それaustin-group(POSIXの背後にある機関)メーリングリストについて話し合ってください。昨年。 (boshFreeBSDshとNetBSDマネージャを含む)議論はsh合意に達する前に終わりましたが、一般的な見解では、POSIXは!コマンドの終了状態を否定することによって文書化されているようにこの動作を要求しました。breakこれはコマンドの終了ステータスを否定する特別な組み込みコマンドです。でした。 0 終了状態で終了します。

しかし、例えば、同じ推論を適用すると、return声明に合うシェルが少ないことがわかります。

では、代わりに匿名関数をzsh使用できます。returnbreak

$ () for i in x y; do echo $i; return 1; done
x
$ echo $?
1

答え2

次のようなことができます

failed=false
for task in "${tasks[@]}"; do
  if ! process "$task"; then
      failed=true
      break
  fi
  commit "$task"
done

if "$failed"; then
   echo "Failed something" >&2
fi

答え3

私が想像する解決策は次のとおりです。

run_til_failure() {
  local didBreak=0
  for task in [TASKS]; do
    process "$task" || { didBreak=1; break; }
    commit "$task"
  done
  local loopExit=$?
  if (( loopExit )); then return $loopExit; fi
  return $didBreak
}

関連情報