如何在while/if等出现错误时使Bash脚本失败?

4
我使用Groovy运行Jenkins流水线作业,并为每个步骤调用Bash脚本。当其中任何一个步骤出现错误时,我希望整个作业都会失败。
对于Groovy,我使用"returnStatus: true"。
对于Bash,我使用"set -e"。
但是,如果Bash脚本中有一个while语句出现错误,使用"set -e"也无法退出。根据Linux手册页上的说明,实际上应该退出。我想知道如何在这种情况下立即退出。
以下是脚本内容:
[jenkins-user@jenkins ~]$ cat script.sh
#!/bin/bash

set -xe

FILE=commands.txt

echo "echos before while"

# Run the commands in the commands file
while read COMMAND
do
    $COMMAND
done < $FILE
echo $?

echo "echos after faulty while"

假设“commands.txt”不存在。 运行脚本:
[jenkins-user@jenkins ~]$ sh script.sh
echos before while
script.sh: line 13: commands.txt: No such file or directory
1
echos after faulty while
[jenkins-user@jenkins ~]$ echo $?
0

尽管while语句返回退出代码1,但脚本仍会继续并成功结束,随后使用echo $?进行检查。
以下是我在使用bash/python等命令/脚本执行步骤后强制Groovy失败的方法:
pipeline {
    agent any
    stages {
        stage("A") {
            steps {
                script {
                    def rc = sh(script: "sh A.sh", returnStatus: true)
                    if (rc != 0)  {
                        error "Failed, exiting now..."
                    }
                }
            }
        }
    }
}

第一个问题,我怎样才能使SHELL脚本在while/if等语句发生错误时失败?我知道可以使用command || exit 1,但如果脚本中有数十个这样的语句,这种方法似乎不够优雅。

第二个问题,我的Groovy错误处理是否正确?有人能提出更好的方式吗?或者也许有Jenkins插件/官方方法来完成这个任务?


有点晚了,但你解决这个问题了吗?即自动检测bash内置命令中的错误?谢谢告诉我“while”不被“-e”覆盖。在使用之前,还有一个理由要进行防御性编码,即[[ -f $FILE ]] - akauppi
4个回答

1

第一个问题,这个链接可能会有帮助 如果任何命令返回非零值,则中止shell脚本

第二个问题:您可以使用try和catch来改进错误处理以进行异常处理。

 try{
       def rc = sh(script: "sh A.sh", returnStatus: true)
       if (rc != 0)  {
                error "Failed, exiting now..."
       }
    }  
    catch (Exception er){
         errorMessage = er.getMessage();
    }

你能解释一下为什么要在顶部添加try/catch吗? - Erez Gedalyahu
不要在顶部添加try/catch,而是在您的stage块中添加try/catch。使用try/catch有助于将错误处理与常规代码分离。 - niri9428480

1
关于Bash脚本。
您的问题在于失败重定向没有中止Bash脚本,尽管使用了set -e。我自己也感到惊讶。但这是我对set -e的第一个失望,所以现在我考虑不再信任它,而是滥用像$command || exit 1这样的东西...
在这里,您可以执行以下操作:
set -xe -o pipefail
cat $FILE | while read command; do $command ; done

但整个循环应该简化为:
bash $FILE

1
为了清晰(以及像我这样粗心的读者):将“-o pipefail”添加到原始问题中不会改变其行为。需要将其转换为管道。 - akauppi

0

为什么不使用while退出代码并返回它呢?(请参见您的脚本的修改版本,最后几行)

[jenkins-user@jenkins ~]$ cat script.sh
#!/bin/bash

set -xe

FILE=commands.txt

echo "echos before while"

# Run the commands in the commands file
while read COMMAND
do
    $COMMAND
done < $FILE
status=$?

echo "echos after faulty while"

exit $status

0
[jenkins-user@jenkins ~]$ cat script.sh
#!/bin/bash

set -xe

FILE=commands.txt

echo "echos before while"

# Run the commands in the commands file
while read COMMAND
do
    $COMMAND
done < $FILE
echo $?

echo "echos after faulty while"

当您在此脚本之后执行echo $?时,它将始终为0,因为最后一个命令是echo "echos after faulty while"。您可以在脚本末尾添加exit 1。在exit 1中,数字1将是错误代码,您可以使用其他数字。因此,脚本将变成:

[jenkins-user@jenkins ~]$ cat script.sh
#!/bin/bash

set -xe

FILE=commands.txt

echo "echos before while"

# Run the commands in the commands file
while read COMMAND
do
    $COMMAND
done < $FILE
echo $?
exit 1

我认为发帖者的观点是它不应该到达echo“ faulty while之后的echo” 这一行,因为他们在顶部有一个set -xe; 当while循环失败时,应该退出。 - Simon Rose

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接