我试图获取我在 Bashwhile
循环的“条件”部分中重复调用的函数的退出代码:
while <function>; do
<stuff>
done
当此循环由于错误而终止时,我需要 的退出代码<function>
。关于如何得到它有什么想法吗?
答案1
您可以捕获条件的退出值并将其向前传播:
while rmdir FOO; ss=$?; [[ $ss -eq 0 ]]
do
echo in loop
done
echo "out of loop with ?=$? but ss=$ss"
输出
rmdir: failed to remove 'FOO': No such file or directory
out of loop with ?=0 but ss=1
在本例中,退出状态rmdir FOO
已被捕获在变量中ss
,并且为1
。 (尝试替换rmdir FOO
为( exit 4 )
。你会发现ss=4
。)
这是如何运作的?请记住,语法实际上是while list-1; do list-2; done
,而不是更常见的期望while command; do list; done
。可以list-1
是一系列以分号分隔的命令,并且文档指出“while
list-2
只要列表中的最后一个命令list-1
返回退出状态为零,该命令就会连续执行该列表。”
作为看起来混乱的while
条件的另一种表示方式,可以在表达式内部分配变量(( ... ))
,然后使用结果。这给出了更难阅读但更紧凑的分配和测试结构:
while rmdir FOO; ((! (ss=$?)))
do
echo in loop
done
echo "out of loop with ?=$? but ss=$ss"
或者您可以使用while rmdir FOO; ! (( ss=$? ))
.这些之所以有效,是因为 ((1)) 的算术计算结果为 1,这通常与 true 相关,因此该计算的退出代码为 0(成功)。另一方面,((0)) 的算术计算结果为 0,这通常与 false 相关,因此该计算的退出代码为 1(失败)。这可能看起来很令人困惑,因为毕竟两个评估((.))
都是“成功”的,但这是一种 hack,使表示 true/false 的算术表达式的值与 bash 的成功/失败退出代码保持一致,并制作条件表达式,如if ...; then ...; fi
、while ...; do ...; done
等,无论是基于退出代码还是算术值,都可以正常工作。
答案2
实现此目的的一种方法是显式进行测试,而不是将其委托给while
.因此,应该执行以下操作:
err=0
while true
do
<function>
(( (err=$?) > 0 )) && break
<stuff>
done
echo "$err"
在(先验无限)循环开始时,您将“手动”执行条件命令并存储退出代码,检查它,如果它非零(表示失败)则退出循环。仅当它为零时,您才执行“实际”循环代码。
语法可以“压缩”,因为(正如 @roaima 所指出的)您可以在(( ... ))
算术测试构造中进行变量赋值。
答案3
创建一个包装函数,用函数的退出代码的结果设置一个变量。例子:
e=0
# file does not exist
foo() {
rm xyz
}
baz() {
foo
e=$?
[[ $e -ne 0 ]] && return 1
return 0
}
# create a file
touch xyz
while baz; do
# first loop succeeds, the file exists
# second loop exits with error, file doesn't exists
echo in loop
done
in loop
rm: cannot remove 'xyz': No such file or directory
# then
$ echo $e
1
答案4
使用zsh
,您可以return
从匿名函数中使用:
() while ((1)) {
<function> || return
<stuff>
}
print $?
对于任何类似 Bourne 的 shell,您始终可以使用命名函数:
repeat_until_fail()
while true; do
<function> || return
<stuff>
done
repeat_until_fail
echo "$?"