我有一个 bash 脚本,它使用set -o errexit
以便在出错时整个脚本在失败时退出。
该脚本运行的curl
命令有时无法检索预期的文件 - 但是当发生这种情况时,脚本不会错误退出。
我已经添加了一个for
循环
- 暂停几秒钟,然后重试该
curl
命令 - 在 for 循环的底部使用
false
来定义默认的非零退出状态 - 如果curl 命令成功 - 循环将中断并且最后一个命令的退出状态应为零。
#! /bin/bash
set -o errexit
# ...
for (( i=1; i<5; i++ ))
do
echo "attempt number: "$i
curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
if [ -f ~/.vim/autoload/pathogen.vim ]
then
echo "file has been retrieved by curl, so breaking now..."
break;
fi
echo "curl'ed file doesn't yet exist, so now will wait 5 seconds and retry"
sleep 5
# exit with non-zero status so main script will errexit
false
done
# rest of script .....
问题是当curl
命令失败时,循环会重试该命令五次 - 如果所有尝试均不成功,则 for 循环结束并且主脚本恢复 - 而不是触发errexit
.
如果此语句失败,如何让整个脚本退出curl
?
答案1
代替:
done
和:
done || exit 1
for
如果循环以非零退出代码退出,这将导致代码退出。
顺便说一下,不需要1
in 。如果下载失败,exit 1
普通exit
命令将以最后执行的命令的退出状态退出,该状态将为(code=1)。false
如果下载成功,则循环的退出代码就是echo
命令的退出代码。 echo
通常以 code=0 退出,表示成功。在这种情况下,||
不会触发并且exit
不会执行该命令。
最后,请注意,这set -o errexit
可能充满惊喜。有关其优缺点的讨论,请参阅格雷格的常见问题解答 #105。
文档
从man bash
:
为了((表达式1;表达式2;表达式3));做列表 ; 完毕
首先,算术表达式 expr1 根据下面算术求值中描述的规则求值。然后重复计算算术表达式 expr2,直到其计算结果为零。每次 expr2 计算结果为非零值时,都会执行 list 并计算算术表达式 expr3。如果省略任何表达式,则其计算结果就像是 1。 返回值是列表中执行的最后一个命令的退出状态,如果任何表达式无效,则返回 false。[强调已添加]
答案2
如果您希望在第一次失败时停止循环,并避免set -e
,您可以执行以下操作:
for i in `seq 1 10`; do
run_command || exit 1;
done
答案3
如果您已errexit
设置,则该false
语句应导致脚本立即退出。如果curl
命令失败,同样的情况。
您的示例脚本,如所写,如果设置了 errexit,则应curl
在第一次调用时在第一个命令失败后退出。false
要查看它是如何工作的(我使用简写-e
来设置errexit
:
$ ( set -e; false; echo still here )
$
$ ( set +e; false; echo still here )
still here
$
因此,如果该curl
命令执行多次,则该脚本没有errexit
设置。
答案4
set -o errexit
在循环和子 shell 中可能会很棘手,因为您必须从进程中返回。
打破循环(即使在正常操作中)被认为是不好的做法。你可以说我是老派,在两个条件下更喜欢使用 while 循环而不是 for 循环,但我发现阅读以下内容更好:
i=1
RET=-1
while [ $i -le 5 ] && [ $RET -ne 0 ]; do
[ $i -eq 1 ] || sleep 5
echo "attempt number: "$i
curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
RET=$?
i=$((i+1))
done
exit $RET