我正在编写一个 shell 脚本,需要执行一堆命令,并且每个命令都依赖于之前的每个命令。如果任何命令失败,整个脚本就会失败,我会调用退出函数。我可以检查每个命令的退出代码,但我想知道是否有可以启用的模式或让 bash 自动执行此操作的方法。
例如,采用以下命令:
cd foo || myfunc
rm a || myfunc
cd bar || myfunc
rm b || myfunc
有没有一种方法可以让我在执行这些命令之前以某种方式向 shell 发出信号,如果其中任何一个命令失败,它应该调用 myfunc,这样我就可以编写一些更清晰的内容,例如:
cd foo
rm a
cd bar
rm b
答案1
你可以使用bash陷阱错误如果任何命令返回大于零的状态,则使脚本退出并在退出时执行您的函数。
就像是:
myfunc() {
echo 'Error raised. Exiting!'
}
trap 'myfunc' ERR
# file does not exist, causing error
ls asd
echo 123
注意猛击陷阱 ERR
隐式set -o errexit
or set -e
and 不是 POSIX。
如果失败的命令是紧跟在or关键字之后的命令列表的一部分、是在或保留字之后的测试的一部分、在或列表中执行的命令的一部分,或者如果命令的返回状态正在使用反转,则ERR
陷阱不会执行。until
while
if
elif
&&
||
!
答案2
已接受答案的(也许)更简单的变化:
- 用于
set -e
导致一个命令失败而中止列表的执行。 - 只需列出您的命令即可。
- 使用
if
-then
-else
语句来执行错误处理命令。最后一块有点棘手。手表:
设置-e 如果 命令1 # 例如,cd foo 命令2 # 例如,rm a 命令3 # 例如,cd 酒吧 命令4 # 例如,rm b 然后 设置+e 成功后执行的命令(如果有) 别的 设置+e 我的函数 失败时执行的其他命令(如果有) 菲
棘手的部分是你输入命令
进入那个if
部分的if
- then
- else
,而不是then
部分或else
部分。回想起那个if
语句的语法是
如果 列表;然后 列表;[ 埃利夫 列表;然后 列表;] ... [ 别的 列表;] 菲 ↑↑↑↑告诉
set -e
shell,如果( ) 失败,它不应该继续执行 ( ),依此类推。如果这种情况发生在 shell 脚本最外层的命令上,shell 将退出。然而,由于......是 一个(复合)列表,后面跟着一个cmd1
cd foo
cmd2
rm a
cmd1
cmd2
cmd3
cmd4
if
,这四个命令中任何一个的失败只会导致整个列表失败 - 这会导致子句else
被执行。如果所有四个命令都成功,则then
执行该子句。
e
无论哪种情况,您应该做的第一件事可能是通过执行以下操作来禁用(关闭)该选项set +e
。否则,如果命令myfunc
失败,脚本可能会被炸出水面。
set -e
是POSIX 规范中指定和描述。
答案3
接受你的话”每个命令都依赖于之前的每个命令。如果任何命令失败,整个脚本都会失败“从字面上看,我认为你不需要任何特殊的函数来处理错误。
您所需要做的就是将命令与&&
运算符和||
运算符链接起来,这正是您所编写的。
例如,如果出现以下情况,该链将断裂并打印“出问题了”任何之前的命令损坏了(bash 从左到右读取)
cd foo && rm a && cd bar && rm b || echo "something went wrong"
真实示例(我创建了 dir foo、文件 a、dir bar 和文件 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 意味着成功。
如果链中的任何命令出错,则命令/脚本/后面的任何内容||将被执行。
仅供记录,如果您需要根据中断的命令执行不同的操作,您也可以使用经典脚本来执行此操作,方法是监视$?
报告前一个命令的退出代码的值(如果命令执行成功,则返回零如果命令失败则为其他正数)
例子:
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