在 Bash 脚本中,如果发生某种情况,如何退出整个脚本?

发布于 2024-08-04 07:22:04 字数 164 浏览 13 评论 0原文

我正在 Bash 中编写一个脚本来测试一些代码。但是,如果编译代码一开始就失败,那么运行测试似乎很愚蠢,在这种情况下我将中止测试。

有没有一种方法可以做到这一点,而无需将整个脚本包装在 while 循环内并使用中断?类似dun dun dun goto 的东西吗?

I'm writing a script in Bash to test some code. However, it seems silly to run the tests if compiling the code fails in the first place, in which case I'll just abort the tests.

Is there a way I can do this without wrapping the entire script inside of a while loop and using breaks? Something like a dun dun dun goto?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

悲歌长辞 2024-08-11 07:22:04

尝试以下语句:

exit 1

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

Try this statement:

exit 1

Replace 1 with appropriate error codes. See also Exit Codes With Special Meanings.

眼藏柔 2024-08-11 07:22:04

使用 set -e

#!/bin/bash

set -e

/bin/command-that-fails
/bin/command-that-fails2

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

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

#!/bin/bash

# I'm assuming you're using make

cd /project-dir
make
if [[ $? -ne 0 ]] ; then
    exit 1
fi

cd /project-dir2
make
if [[ $? -ne 0 ]] ; then
    exit 1
fi

使用 set -e 时,它将如下所示:

#!/bin/bash

set -e

cd /project-dir
make

cd /project-dir2
make

任何失败的命令都会导致整个脚本失败并返回您可以使用$?检查退出状态。如果你的脚本很长或者你正在构建很多东西,那么如果你到处添加返回状态检查,它会变得非常难看。

Use set -e

#!/bin/bash

set -e

/bin/command-that-fails
/bin/command-that-fails2

The script will terminate after the first line that fails (returns nonzero exit code). In this case, command-that-fails2 will not run.

If you were to check the return status of every single command, your script would look like this:

#!/bin/bash

# I'm assuming you're using make

cd /project-dir
make
if [[ $? -ne 0 ]] ; then
    exit 1
fi

cd /project-dir2
make
if [[ $? -ne 0 ]] ; then
    exit 1
fi

With set -e it would look like:

#!/bin/bash

set -e

cd /project-dir
make

cd /project-dir2
make

Any command that fails will cause the entire script to fail and return an exit status you can check with $?. If your script is very long or you're building a lot of stuff it's going to get pretty ugly if you add return status checks everywhere.

天涯离梦残月幽梦 2024-08-11 07:22:04

一位 SysOps 人员曾经教过我三指爪技术:

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

这些功能对于 *NIX 操作系统和 shell 风格来说是稳健的。将它们放在脚本(bash 或其他脚本)的开头,try() 语句和代码上。

解释

(基于飞羊评论)。

  • yell:打印脚本名称和所有参数到 stderr
    • $0 是脚本的路径;
    • $* 都是参数。
    • >&2 表示 > 将 stdout 重定向到 &管道2管道1将是stdout本身。
  • dieyell 的作用相同,但以非 0 退出状态退出,这意味着“失败”。
  • try 使用 ||(布尔值 OR),如果左侧失败,则仅评估右侧。
    • $@ 又是所有参数,但不同

A SysOps guy once taught me the Three-Fingered Claw technique:

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

These functions are *NIX OS and shell flavor-robust. Put them at the beginning of your script (bash or otherwise), try() your statement and code on.

Explanation

(based on flying sheep comment).

  • yell: print the script name and all arguments to stderr:
    • $0 is the path to the script ;
    • $* are all arguments.
    • >&2 means > redirect stdout to & pipe 2. pipe 1 would be stdout itself.
  • die does the same as yell, but exits with a non-0 exit status, which means “fail”.
  • try uses the || (boolean OR), which only evaluates the right side if the left one failed.
分开我的手 2024-08-11 07:22:04

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

如果使用 exit 来代替,则当使用 source 调用脚本时,将导致退出启动该脚本的 shell,但可执行脚本只会终止,如预期的那样。

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

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

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

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

If you will invoke the script with source, you can use return <x> where <x> will be the script exit status (use a non-zero value for error or false). But if you invoke an executable script (i.e., directly with its filename), the return statement will result in a complain (error message "return: can only `return' from a function or sourced script").

If exit <x> is used instead, when the script is invoked with source, it will result in exiting the shell that started the script, but an executable script will just terminate, as expected.

To handle either case in the same script, you can use

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

This will handle whichever invocation may be suitable. That is assuming you will use this statement at the script's top level. I would advise against directly exiting the script from within a function.

Note: <x> is supposed to be just a number.

抹茶夏天i‖ 2024-08-11 07:22:04

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

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

I often include a function called run() to handle errors. Every call I want to make is passed to this function so the entire script exits when a failure is hit. The advantage of this over the set -e solution is that the script doesn't exit silently when a line fails, and can tell you what the problem is. In the following example, the 3rd line is not executed because the script exits at the call to false.

function run() {
  cmd_output=$(eval $1)
  return_value=$?
  if [ $return_value != 0 ]; then
    echo "Command $1 failed"
    exit -1
  else
    echo "output: $cmd_output"
    echo "Command succeeded."
  fi
  return $return_value
}
run "date"
run "false"
run "date"
美羊羊 2024-08-11 07:22:04

您可以利用短路评估if 构造>:

#!/usr/bin/env bash

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

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

Instead of if construct, you can leverage the short-circuit evaluation:

#!/usr/bin/env bash

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

Note the pair of parentheses which is necessary because of priority of alternation operator. $? is a special variable set to exit code of most recently called command.

一笑百媚生 2024-08-11 07:22:04
#!/bin/bash -x

# exit and report the failure if any command fails
exit_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 捕获最后执行的步骤并捕获该步骤的返回代码。这是可用于任何清理的函数,类似于 shutdownhooks

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

文档


第 2 部分:

trap [action] [signal]

退出信号


第 3 部分:

如果一系列一个或多个命令返回非零状态,则立即退出。如果失败的命令是紧跟在 while 或until 关键字之后的命令列表的一部分、if 语句中测试的一部分、&& 中执行的任何命令的一部分,则 shell 不会退出。或||列出最后 && 后面的命令除外或 ||,管道中除最后一个命令之外的任何命令,或者命令的返回状态与 ! 反转。如果子 shell 之外的复合命令由于在 -e 被忽略时命令失败而返回非零状态,则 shell 不会退出。如果设置了 ERR 陷阱,则会在 shell 退出之前执行。

文档


第 4 部分:

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

source common.sh
#!/bin/bash -x

# exit and report the failure if any command fails
exit_trap () {                                         # ---- (1)
  local lc="$BASH_COMMAND" rc=$?
  echo "Command [$lc] exited with code [$rc]"
}

trap exit_trap EXIT                                    # ---- (2)

set -e                                                 # ---- (3)

Explanation:

This question is also about how to write clean code. Let's divide the above script into multiple parts:


Part - 1:
exit_trap is a function that gets called when any step failed and captures the last executed step using $BASH_COMMAND and captures the return code of that step. This is the function that can be used for any clean-up, similar to shutdownhooks

The command currently being executed or about to be executed, unless the shell is executing a command as the result of a trap, in which case it is the command executing at the time of the trap.

Doc.


Part - 2:

trap [action] [signal]

Register the trap action (here exit_trap function) in case of EXIT signal.


Part - 3:

Exit immediately if a sequence of one or more commands returns a non-zero status. The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test in an if statement, part of any command executed in a && or || list except the command following the final && or ||, any command in a pipeline but the last, or if the command’s return status is being inverted with !. If a compound command other than a subshell returns a non-zero status because a command failed while -e was being ignored, the shell does not exit. A trap on ERR, if set, is executed before the shell exits.

Doc.


Part - 4:

You can create a common.sh file and source it in all of your scripts.

source common.sh
偏闹i 2024-08-11 07:22:04

我有同样的问题,但不能问,因为它会重复。

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

这是一个关于如何执行此操作的小脚本:

#!/bin/bash

boom() {
    while true; do sleep 1.2; echo boom; done
}

f() {
    echo Hello
    N=0
    while
        ((N++ <10))
    do
        sleep 1
        echo $N
        #        ((N > 5)) && exit 4 # does not work
        ((N > 5)) && { kill -9 $; exit 5; } # works 
    done
}

boom &
f &

while true; do sleep 0.5; echo beep; done

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

I have the same question but cannot ask it because it would be a duplicate.

The accepted answer, using exit, does not work when the script is a bit more complicated. If you use a background process to check for the condition, exit only exits that process, as it runs in a sub-shell. To kill the script, you have to explicitly kill it (at least that is the only way I know).

Here is a little script on how to do it:

#!/bin/bash

boom() {
    while true; do sleep 1.2; echo boom; done
}

f() {
    echo Hello
    N=0
    while
        ((N++ <10))
    do
        sleep 1
        echo $N
        #        ((N > 5)) && exit 4 # does not work
        ((N > 5)) && { kill -9 $; exit 5; } # works 
    done
}

boom &
f &

while true; do sleep 0.5; echo beep; done

This is a better answer but still incomplete a I really don't know how to get rid of the boom part.

掩于岁月 2024-08-11 07:22:04

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

对于软退出执行

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

对于硬退出执行

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

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

You can close your program by program name on follow way:

for soft exit do

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

for hard exit do

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

If you like to know how to evaluate condition for closing a program, you need to customize your question.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文