有没有一种方法可以让我的bash脚本在任何命令错误时自动运行?


12

我正在编写一个需要执行一系列命令的shell脚本,每个命令都取决于每个先前的命令。如果有任何命令失败,则整个脚本将失败,我将调用退出函数。我可以检查每个命令的退出代码,但是我想知道是否存在可以启用的模式或一种使bash自动执行此操作的方法。

例如,使用以下命令:

cd foo || myfunc
rm a || myfunc
cd bar || myfunc
rm b || myfunc


有没有一种方法可以在执行这些命令之前以某种方式向外壳发出信号,如果它们中的任何一个失败,它应该调用myfunc,这样我可以编写一些更简洁的代码,例如:

cd foo
rm a
cd bar
rm b

Answers:


13

您可以使用bash 陷阱ERR如果任何命令返回的状态大于零,则使脚本退出,并在退出时执行功能。

就像是:

myfunc() {
  echo 'Error raised. Exiting!'
}

trap 'myfunc' ERR

# file does not exist, causing error
ls asd
echo 123

请注意,bash陷阱 ERR不会隐式set -o errexitset -e不是POSIX。

并且ERR不执行陷阱,如果失败的命令是命令列表的一部分后立即untilwhile关键字,以下的测试的一部分ifelif保留字,命令的一部分执行&&||列表,或者命令的返回状态被倒置!


1

接受答案的一个(也许)更简单的变化:

  1. 使用set -e 造成一个命令失败中止列表的执行。
  2. 只需列出您的命令。
  3. 使用一个if- then- else要执行的语句您的错误处理指令(S)。最后一块有点棘手。看:
设置-e
如果
    cmd 1                         #例如cd foo
     cmd 2                         #例如rm a
     cmd 3                         #例如cd bar
     cmd 4                         #例如rm b
然后
    设置+ e
    成功执行的命令(如果有)
其他
    设置+ e
    myfunc
    其他命令失败时(如果有的话)执行 
网络连接

最棘手的部分是,你把你的命令 if部分的的if- - thenelse而不是then部分或else一部分。回想一下if语句的语法

如果 清单 ; 然后 列出 ; [  清单 ; 然后 列出 ; ] ... [其他 清单 ; ] fi 
   ↑↑↑↑
set -e告诉的是,如果shell ()失败,它不应该去和执行 (),等下了线。如果这在Shell脚本最外层的命令上发生,则Shell将退出。然而,由于· · · 是以下内容的一个(化合物)列表,任何这些四个命令失败简单地使整个列表失败-这将导致要被执行子句。如果所有四个命令都成功,则执行该子句。cmd1cd foocmd2rm acmd1cmd2cmd3cmd4ifelsethen

无论哪种情况,您都应该做的第一件事就是禁用(关闭)该e选项set +e。否则,如果输入命令myfunc失败,脚本可能会被淹没。

set -e指定,并且在POSIX说明书中描述


0

用你的话说:“ 每个命令都取决于每个先前的命令。如果任何命令失败,则整个脚本都应该失败 ”,我认为您不需要任何特殊功能来处理错误。

您所需要做的就是将命令与&&operator和||operator链接起来,这与您编写的内容完全相同。

例如,如果先前的任何命令中断,则此链将中断并打印“出问题了” (bash从左到右读取)

cd foo && rm a && cd bar && rm b || echo "something went wrong"

真实示例(我为真实演示创建了dir foo,文件a,目录栏和文件b):

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm a && cd bar && rm bb || echo "something is wrong"
rm: cannot remove 'bb': No such file or directory
something is wrong #mind the error in the last command

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm aa && cd bar && rm b || echo "something is wrong"
rm: cannot remove 'aa': No such file or directory
something is wrong #mind the error in second command in the row

最后,如果所有命令都已成功执行(退出代码0),则脚本继续运行:

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm a && cd bar && rm b || echo "something is wrong"
gv@debian:/home/gv/Desktop/PythonTests/foo/bar$ 
# mind that the error message is not printed since all commands were successful.

要记住的重要一点是,如果前一个命令以代码0退出(对于bash表示成功),则执行&& next命令。

如果链中有任何命令出错,则命令/脚本/后面的内容|| 将被执行。

仅作记录,如果需要根据中断的命令执行不同的操作,则还可以使用经典脚本来执行此操作,方法是监视$?报告上一命令的退出代码的值(如果命令成功执行,则返回零)或其他正数(如果命令失败)

例:

for comm in {"cd foo","rm a","cd bbar","rm b"};do  #mind the error in third command
eval $comm
    if [[ $? -ne 0 ]];then 
        echo "something is wrong in command $comm"
        break
    else 
    echo "command $comm executed succesful"
    fi
done

输出:

command cd foo executed succesfull
command rm a executed succesfull
bash: cd: bbar: No such file or directory
something is wrong in command cd bbar

提示:您可以通过应用以下消息取消显示“ bash:cd:bbar:No such file ...”消息 eval $comm 2>/dev/null

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.