当我跳出循环时触发非零退出代码

当我跳出循环时触发非零退出代码

我想运行一系列任务,但如果其中任何一个失败就停止,因此我写了(类似的东西):

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

这工作得很好,但是(如指定的)即使我们提前中断,该循环的退出状态也为零。理想情况下break-ing 能够传达失败。

我知道返回0是 的记录行为break,但我很好奇是否有任何相对干净的解决方法。我能想象的最好的办法是将其包装在一个函数中并设置一个didBreak变量,并将其用作(函数的)退出状态。它可以工作,但感觉过于复杂。

答案1

在许多 shell 中使用! break都可以使用(除了基于 pdksh 的 shell 和shFreeBSD 的 shell(经过 设计)在我的测试中):

$ 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 背后的主体)邮件列表上讨论去年。讨论(涉及bosh、 FreeBSDsh和 NetBSD的维护者sh)在达成共识之前就结束了,但普遍的观点似乎是 POSIX 要求这种行为,如!记录的那样否定命令的退出状态,并且break是一个特殊的内置命令,以 0 退出状态退出。

但是,如果您将相同的推理应用于return例如,您会发现符合要求的外壳更少。

在中zsh,您可以使用return匿名函数代替break

$ () 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
}

相关内容