使用 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
任何失败的命令都会导致整个脚本失败并返回一个退出状态,你可以用 $? 检查。如果您的脚本很长,或者您正在构建很多东西,那么如果您在各处添加返回状态检查,它将会变得非常难看。
set -e
,您仍然可以让 一些 命令在不停止脚本的情况下退出并出现错误:command 2>&1 || echo $?
。
set -e
将中止脚本。例如,仅当 foo
和 bar
返回非零值时,foo || bar
才会失败。通常,如果您在开始时添加 set -e
,则编写良好的 bash 脚本将起作用,并且添加作为自动完整性检查:如果出现任何问题,则中止脚本。
set -o pipefail
选项,如果其中任何一个失败,您也可能失败。
set -e
的惯用代码就是 make || exit $?
。
一个 SysOps 人曾经教过我三指爪技术:
yell() { echo "$0: $*" >&2; }
die() { yell "$*"; exit 111; }
try() { "$@" || die "cannot $*"; }
这些功能是*NIX OS 和shell 风格强大的。将它们放在脚本的开头(bash 或其他),try()
您的语句和代码。
解释
(基于 flying sheep 评论)。
喊:打印脚本名称和 stderr 的所有参数:$0 是脚本的路径; $* 都是参数。 >&2 表示 > 将标准输出重定向到 & 管道 2。管道 1 本身就是标准输出。
$0 是脚本的路径;
$* 都是参数。
>&2 表示 > 将标准输出重定向到 & 管道 2。管道 1 本身就是标准输出。
die 和yyy 一样,但是以非0 的退出状态退出,这意味着“失败”。
尝试使用 || (布尔 OR),仅在左侧失败时评估右侧。 $@ 又是所有参数,但不同。
$@ 又是所有参数,但不同。
$0
是脚本的路径。 $*
都是参数。 >&2
表示“>
将标准输出重定向到 &
管道 2
”。管道 1 本身就是标准输出。所以 yell 用脚本名称作为所有参数的前缀并打印到 stderr。 die 和 yell 一样,但是以非 0 的退出状态退出,这意味着“失败”。 try 使用布尔值或 ||
,它仅在左侧没有失败时评估右侧。 $@
又是所有参数,但 different。希望能解释一切
yell
和 die
。然而,try
不是那么多。你能提供一个你使用它的例子吗?
try
函数。例如:try cp ./fake.txt ./copy.txt
如果您将使用 source
调用脚本,则可以使用 return <x>
,其中 <x>
将是脚本退出状态(使用非零值表示错误或 false)。但是如果你调用一个可执行脚本(即直接使用它的文件名),return 语句将导致一个抱怨(错误消息“return: can only `return' from a function or sourced script”)。
如果改为使用 exit <x>
,当使用 source
调用脚本时,将导致退出启动脚本的 shell,但可执行脚本将按预期终止。
要在同一脚本中处理任何一种情况,您可以使用
return <x> 2> /dev/null || exit <x>
这将处理可能适合的任何调用。假设您将在脚本的顶层使用此语句。我建议不要直接从函数中退出脚本。
注意:<x>
应该只是一个数字。
我经常包含一个名为 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"
set -e option
。然后是命令,因为它带有参数,所以我使用单引号来避免像这样的 bash 问题:runTry 'mysqldump $DB_PASS --user="$DB_USER" --host="$BV_DB_HOST" --triggers --routines --events --single-transaction --verbose $DB_SCHEMA $tables -r $BACKUP_DIR/$tables$BACKUP_FILE_NAME'
。注意我将函数的名称更改为 runTry。
eval
可能会很危险,但否则这看起来很不错。
您可以利用 short-circuit evaluation 代替 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]
请注意由于交替运算符的优先级而必需的括号对。 $?
是一个特殊变量,设置为退出最近调用的命令的代码。
command -that --fails || exit $?
,它可以不带括号,那么 echo $[4/0]
是什么导致我们需要它们?
echo $[4/0] || exit $?
)bash 将永远不会执行 echo
,更不用说遵守||
。
我有同样的问题,但不能问,因为它会重复。
当脚本有点复杂时,使用 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
这是一个更好的答案,但仍然不完整,我真的不知道如何摆脱繁荣部分。
您可以通过以下方式按程序名称关闭程序:
对于软退出做
pkill -9 -x programname # Replace "programmname" by your programme
硬退出做
pkill -15 -x programname # Replace "programmname" by your programme
如果您想知道如何评估关闭程序的条件,您需要自定义您的问题。
不定期副业成功案例分享
1
。如果脚本打算由另一个脚本运行,您可能需要定义自己的一组具有特定含义的状态代码。例如,1
== 测试失败,2
== 编译失败。如果脚本是其他东西的一部分,您可能需要调整代码以匹配那里使用的做法。例如,当 automake 运行部分测试套件时,代码77
用于标记已跳过的测试。exit #
命令放在函数中,而不是脚本中。 (在这种情况下,请改用return #
。)exit
命令仅退出正在运行脚本的 bash 进程。如果您使用./script.sh
或bash script.sh
运行脚本,您的“窗口”或外壳将保持打开状态,因为为剧本。另一方面,如果您使用. script.sh
或source script.sh
运行脚本,它会在您当前的 bash 实例中执行并退出它。