Answers:
exit #
命令放在函数而不是脚本中。(在这种情况下,请使用return #
。)
exit 0
退出脚本并返回0(告诉其他可能使用此脚本结果的脚本成功)
使用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
返回非零值。通常,编写良好的bash脚本将在您set -e
开始添加时起作用,并且该添加将作为自动的健全性检查起作用:如果出现任何问题,请中止该脚本。
set -o pipefail
选项也可能导致命令失败。
set -e
将是公正的make || exit $?
。
SysOps的一个人曾经教过我三指爪技术:
yell() { echo "$0: $*" >&2; }
die() { yell "$*"; exit 111; }
try() { "$@" || die "cannot $*"; }
这些功能是* NIX OS和shell健壮的。将它们放在脚本的开头(bash或其他方式),try()
语句和代码的开头。
(根据 飞羊的评论)。
yell
:将脚本名称和所有参数打印到stderr
:
$0
是脚本的路径;$*
都是论点。 >&2
表示>
将stdout重定向到&pipe2
。管1
会stdout
本身。die
的功能与相同yell
,但退出状态为非零退出状态,表示“失败”。try
使用||
(boolean OR
),它仅在左侧失败时评估右侧。
$@
又是所有论点,但又不同。die() { yell "$1"; exit $2; }
使您可以通过传递消息并退出代码die "divide by zero" 115
。
yell
和die
。但是,try
没有那么多。您能提供一个使用示例吗?
如果将使用调用脚本source
,则可以使用return <x>
where <x>
将是脚本退出状态(对于错误或false,请使用非零值)。但是,如果您调用可执行脚本(即直接使用其文件名),则return语句将导致抱怨(错误消息“ return:只能从函数或源脚本中返回”)。
如果exit <x>
使用代替,则使用调用脚本时source
,它将导致退出启动脚本的外壳,但是可执行脚本将按预期终止。
要在同一脚本中处理任何一种情况,都可以使用
return <x> 2> /dev/null || exit <x>
这将处理任何合适的调用。假设您将在脚本的顶层使用此语句。我建议不要直接从函数中退出脚本。
注意:<x>
应该只是一个数字。
我经常包括一个名为run()的函数来处理错误。我要进行的每个调用都传递给此函数,因此在遇到故障时会退出整个脚本。与set -e解决方案相比,此方法的优势在于,当行失败时,脚本不会以静默方式退出,并且可以告诉您问题出在哪里。在以下示例中,第三行未执行,因为脚本在调用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
如果您接受任意输入,则可能会很危险,但否则看起来非常不错。
除了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接受的答案不起作用。如果使用后台进程检查条件,则退出仅退出该进程,因为它在子外壳程序中运行。要杀死脚本,必须明确杀死它(至少这是我所知道的唯一方法)。
这是一个有关如何执行此操作的小脚本:
#!/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
这是一个更好的答案,但仍然不完整-我真的不知道该如何摆脱吊杆零件。
1
始终使用。如果该脚本打算由另一个脚本运行,则您可能需要定义自己的一组具有特定含义的状态代码。例如,1
==测试失败,2
==编译失败。如果脚本是其他内容的一部分,则可能需要调整代码以匹配此处使用的实践。例如,当部分测试套件由automake运行时,该代码77
用于标记已跳过的测试。