在Bash脚本中,如果发生特定条件,我如何退出整个脚本?

我正在用Bash编写一个脚本来测试一些代码。然而,如果编译代码首先失败,运行测试似乎很傻,在这种情况下,我将中止测试。

有没有一种方法可以做到这一点,而不会将整个脚本包装在这时循环中并使用中断?像dun dun goto这样的东西?

896367 次浏览

试试这个语句:

exit 1

用适当的错误代码替换1。另请参阅具有特殊含义的退出代码

使用set-e

#!/bin/bash
set -e
/bin/command-that-fails/bin/command-that-fails2

脚本将在第一行失败(返回非零退出代码)后终止。在这种情况下,命令失败2不会运行。

如果您要检查每个命令的返回状态,您的脚本将如下所示:

#!/bin/bash
# I'm assuming you're using make
cd /project-dirmakeif [[ $? -ne 0 ]] ; thenexit 1fi
cd /project-dir2makeif [[ $? -ne 0 ]] ; thenexit 1fi

使用set-e,它看起来像:

#!/bin/bash
set -e
cd /project-dirmake
cd /project-dir2make

任何失败的命令都会导致整个脚本失败并返回一个退出状态,您可以使用$?进行检查。如果您的脚本很长或者您正在构建很多东西,如果您在任何地方添加返回状态检查,它会变得非常丑陋。

而不是if构造,您可以利用短路评估

#!/usr/bin/env bash
echo $[1+1]echo $[2/0]              # division by 0 but execution of script proceedsecho $[3+1](echo $[4/0]) || exit $? # script halted with code 1 returned from `echo`echo $[5+1]

注意,由于交替运算符的优先级,这对括号是必要的。$?是一个特殊的变量,设置为退出最近调用的命令的代码。

我经常包含一个名为run()的函数来处理错误。我想进行的每一次调用都会传递给这个函数,因此当遇到故障时整个脚本都会退出。与set-e解决方案相比,这一方法的优点是当一行失败时脚本不会静默退出,并且可以告诉你问题是什么。在下面的示例中,第3行不会被执行,因为脚本在调用false时退出。

function run() {cmd_output=$(eval $1)return_value=$?if [ $return_value != 0 ]; thenecho "Command $1 failed"exit -1elseecho "output: $cmd_output"echo "Command succeeded."fireturn $return_value}run "date"run "false"run "date"

一个SysOps的家伙曾经教过我三指爪技术:

yell() { echo "$0: $*" >&2; }die() { yell "$*"; exit 111; }try() { "$@" || die "cannot $*"; }

这些函数是*NIX OS和shell风格健壮的。将它们放在脚本的开头(bash或其他方式),try()您的语句和代码。

补充说明

(基于飞羊评论)。

  • yell:将脚本名称和所有参数打印到stderr
    • $0是脚本的路径;
    • $*都是参数。
    • >&2意味着#1将标准输出重定向到&管道#2管道#3将是stdout本身。
  • dieyell相同,但以非0状态退出,这意味着“失败”。
  • try使用||(booleanOR),它仅在左侧失败时计算右侧。
    • $@又是所有参数,但是不同

如果您将使用source调用脚本,您可以使用return <x>,其中<x>将是脚本退出状态(使用非零值表示错误或false)。但是如果您调用可执行脚本(即直接使用其文件名),返回语句将导致投诉(错误消息“返回:只能从函数或源脚本“返回”)。

如果使用exit <x>代替,当使用source调用脚本时,它将导致退出启动脚本的shell,但可执行脚本将像预期的那样终止。

要在同一脚本中处理任何一种情况,您可以使用

return <x> 2> /dev/null || exit <x>

这将处理任何合适的调用。这是假设您将在脚本的顶层使用此语句。我建议不要直接从函数中退出脚本。

注意:<x>应该只是一个数字。

我有同样的问题,但不能问,因为这将是重复的。

接受的答案是使用退出,当脚本有点复杂时不起作用。如果你使用后台进程来检查条件,退出只退出该进程,因为它在子shell中运行。要杀死脚本,你必须显式杀死它(至少这是我知道的唯一方法)。

这里有一个关于如何做到这一点的小脚本:

#!/bin/bash
boom() {while true; do sleep 1.2; echo boom; done}
f() {echo HelloN=0while((N++ <10))dosleep 1echo $N#        ((N > 5)) && exit 4 # does not work((N > 5)) && { kill -9 $$; exit 5; } # worksdone}
boom &f &
while true; do sleep 0.5; echo beep; done

这是一个更好的答案,但仍然不完整,我真的不知道如何摆脱繁荣部分。

您可以按以下方式按程序名称关闭程序:

软退出做

pkill -9 -x programname # Replace "programmname" by your programme

硬出口做

pkill -15 -x programname # Replace "programmname" by your programme

如果您想知道如何评估关闭程序的条件,您需要自定义您的问题。

#!/bin/bash -x
# exit and report the failure if any command failsexit_trap () {                                         # ---- (1)local lc="$BASH_COMMAND" rc=$?echo "Command [$lc] exited with code [$rc]"}
trap exit_trap EXIT                                    # ---- (2)
set -e                                                 # ---- (3)

说明:

这个问题也是关于如何编写干净的代码。让我们将上面的脚本分为多个部分:


第1部分:exit_trap是一个函数,当任何步骤失败时都会被调用,并使用$BASH_COMMAND捕获最后执行的步骤并捕获该步骤的返回代码。这是可用于任何清理的函数,类似于关机挂钩

当前正在执行或即将执行的命令,除非shell正在执行作为陷阱结果的命令,在这种情况下,它是陷阱发生时执行的命令。

文档。


第二部分:

trap [action] [signal]

退出信号的情况下注册陷阱操作(这里是exit_trap函数)。


第三部分:

如果一个或多个命令序列返回非零状态,则立即退出。如果失败的命令是命令列表的一部分,紧随其后或直到关键字,if语句中测试的一部分,在&&或|列表中执行的任何命令的一部分,除了最后一个&&或||之后的命令之外,管道中但最后一个命令的任何命令,或者命令的返回状态正在用!反转,则shell不会退出。如果子shell以外的复合命令返回非零状态,因为在忽略-e时命令失败,shell不会退出。如果设置了ERR上的陷阱,则会在shell退出之前执行。

文档。


第四部分:

您可以创建common.sh文件并在所有脚本中获取它。

source common.sh