我在编写shell脚本方面是个新手。如果命令失败,我想打印一条消息并退出脚本。我试过了:
my_command && (echo 'my_command failed; exit)
但这并不奏效。它继续执行脚本中这一行之后的指令。我使用Ubuntu和bash。
如果my_command是规范设计的,即成功时返回0,那么&&与你想要的完全相反。你需要||。
my_command
&&
||
还要注意,(在bash中对我来说似乎不正确,但我不能从我所在的位置尝试。告诉我。
(
my_command || { echo 'my_command failed' ; exit 1; }
试一试:
四个变化:
{ }
( )
exit
;
{
}
因为你想打印消息并只在命令失败时退出(退出时值为非零),所以你需要||而不是&&。
cmd1 && cmd2
将在cmd1成功时运行cmd2(退出值0)。而
cmd1
cmd2
0
cmd1 || cmd2
将在cmd1失败时运行cmd2(退出值非零)。
使用( )会使它们里面的命令在< em >亚外层< / em >中运行,从那里调用exit会导致你退出子shell,而不是你的原始shell,因此在你的原始shell中继续执行。
要克服这个问题,请使用{ }
bash需要最后两个更改。
还要注意,每个命令的退出状态都存储在shell变量$?,可在执行命令后立即查看。非零状态表示失败:
my_command if [ $? -eq 0 ] then echo "it worked" else echo "it failed" fi
其他答案已经很好地涵盖了直接问题,但你可能也对使用set -e感兴趣。因此,任何失败的命令(在特定上下文之外,如if测试)将导致脚本中止。对于某些脚本,它非常有用。
set -e
if
我把下面这个习语拼凑起来了:
echo "Generating from IDL..." idlj -fclient -td java/src echo.idl if [ $? -ne 0 ]; then { echo "Failed, aborting." ; exit 1; } fi echo "Compiling classes..." javac *java if [ $? -ne 0 ]; then { echo "Failed, aborting." ; exit 1; } fi echo "Done."
在每条命令前加上一个有信息的回显,并在每条命令后加上相同的 if [ $? -ne 0 ];...线。(当然,如果你想,你可以编辑这个错误消息。)
if [ $? -ne 0 ];...
如果您希望脚本中的所有命令都具有这种行为,只需添加
set -e set -o pipefail
在脚本的开头。这对选项告诉bash解释器,当命令返回非零退出码时,就退出。(有关为什么需要pipefail的详细信息,请参阅http://petereisentraut.blogspot.com/2010/11/pipefail.html)
pipefail
但是,这并不允许您打印退出消息。
你也可以使用,如果你想保留退出错误状态,并有一个可读的文件,每行一个命令:
my_command1 || exit my_command2 || exit
但是,这将不会打印任何额外的错误消息。但在某些情况下,失败的命令仍然会打印错误。
trap shell内置允许捕获信号和其他有用的条件,包括失败的命令执行(即非零返回状态)。因此,如果你不想显式地测试每个命令的返回状态,你可以输入trap "your shell code" ERR,并且shell代码将在任何命令返回非零状态时执行。例如:
trap
trap "your shell code" ERR
trap "echo script failed; exit 1" ERR
注意,与捕获失败命令的其他情况一样,管道需要特殊处理;上面不会捕获false | true。
false | true
直接使用exit可能会很棘手,因为脚本可能来自其他地方(例如从终端)。我更喜欢使用subshell和set -e(加上错误应该进入cerr,而不是cout):
set -e ERRCODE=0 my_command || ERRCODE=$? test $ERRCODE == 0 || (>&2 echo "My command failed ($ERRCODE)"; exit $ERRCODE)