如何根据 for 循环的结果使该脚本错误退出?

如何根据 for 循环的结果使该脚本错误退出?

我有一个 bash 脚本,它使用set -o errexit以便在出错时整个脚本在失败时退出。
该脚本运行的curl命令有时无法检索预期的文件 - 但是当发生这种情况时,脚本不会错误退出。

我已经添加了一个for循环

  1. 暂停几秒钟,然后重试该curl命令
  2. 在 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如果循环以非零退出代码退出,这将导致代码退出。

顺便说一下,不需要1in 。如果下载失败,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

相关内容