我想运行一系列任务,但如果其中任何一个失败就停止,因此我写了(类似的东西):
for task in [TASKS]; do
process "$task" || break
commit "$task"
done
这工作得很好,但是(如指定的)即使我们提前中断,该循环的退出状态也为零。理想情况下break
-ing 能够传达失败。
我知道返回0
是 的记录行为break
,但我很好奇是否有任何相对干净的解决方法。我能想象的最好的办法是将其包装在一个函数中并设置一个didBreak
变量,并将其用作(函数的)退出状态。它可以工作,但感觉过于复杂。
答案1
在许多 shell 中使用! break
都可以使用(除了基于 pdksh 的 shell 和sh
FreeBSD 的 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
}