为什么set -e导致我的脚本在遇到以下内容时退出?

时间:2010-01-07 19:17:43

标签: linux bash shell set

我有一个bash脚本,用于检查由cron作业创建的一些日志文件,这些日志文件在文件名中有时间戳(直到第二个)。它使用以下代码:

CRON_LOG=$(ls -1 $LOGS_DIR/fetch_cron_{true,false}_$CRON_DATE*.log 2> /dev/null | sed 's/^[^0-9][^0-9]*\([0-9][0-9]*\).*/\1 &/' | sort -n | cut -d ' ' -f2- | tail -1 )
if [ -f "$CRON_LOG" ]; then
    printf "Checking $CRON_LOG for errors\n"
else
        printf "\n${txtred}Error: cron log for $CRON_NOW does not exist.${txtrst}\n"
        printf "Either the specified date is too old for the log to still be around or there is a problem.\n"
        exit 1
fi
CRIT_ERRS=$(cat $CRON_LOG | grep "ERROR" | grep -v "Duplicate tracking code")
if [ -z "$CRIT_ERRS" ]; then
        printf "%74s[${txtgrn}PASS${txtrst}]\n"
else
        printf "%74s[${txtred}FAIL${txtrst}]\n"
        printf "Critical errors detected! Outputting to console...\n"
        echo $CRIT_ERRS
fi

所以这段代码工作正常,但我现在正在尝试清理我的脚本并在所有脚本的顶部实现set -e。当我对此脚本执行此操作时,它将以错误代码1退出。请注意,我在第一个语句转储到/ dev / null时出错。这是因为有些日子里文件中包含“true”字样,其他日期则为“false”。无论如何,我不认为这是我的问题,因为脚本输出“检查xxxxx.log是否有错误”。在退出之前,我将set -e添加到顶部。

注意:$ CRON_DATE变量是从用户输入派生的。我可以从命令行“$。/ checkcron.sh 01/06/2010”运行完全相同的语句,并且它在脚本顶部没有set -e语句的情况下工作正常。

更新:我在我的脚本中添加了“set -x”并缩小了问题范围。输出的最后一位是:

Checking /map/etl/tektronix/logs/fetch_cron_false_010710054501.log for errors
++ cat /map/etl/tektronix/logs/fetch_cron_false_010710054501.log
++ grep ERROR
++ grep -v 'Duplicate tracking code'
+ CRIT_ERRS=

[1]+  Exit 1                  ./checkLoad.sh...

所以看起来问题出现在这一行:

CRIT_ERRS=$(cat $CRON_LOG | grep "ERROR" | grep -v "Duplicate tracking code")

感谢任何帮助。 :)

谢谢, 莱恩

4 个答案:

答案 0 :(得分:3)

添加set -x会打印脚本执行的痕迹,可以帮助您诊断错误的来源。

修改:

你的grep返回1的退出代码,因为它没有找到“ERROR”字符串。

编辑2:

我对结肠道歉。我没有测试它。

但是,以下工作(我在喷出之前测试了这个)并避免调用外部cat。因为您使用子shell的结果设置变量,并且set -e查看子shell作为一个整体,您可以这样做:

CRIT_ERRS=$(cat $CRON_LOG | grep "ERROR" | grep -v "Duplicate tracking code"; true)

答案 1 :(得分:2)

一旦简单命令以非零退出状态退出,请求set -e使脚本退出。这与您的ls命令有害地结合在一起,当被要求列出不存在的文件时,该命令以非零状态退出,这总是适用于您,因为truefalse变体不共存。

答案 2 :(得分:2)

bash -c 'f=`false`; echo $?'
1
bash -c 'f=`true`; echo $?'
0
bash -e -c 'f=`false`; echo $?'
bash -e -c 'f=`true`; echo $?'
0

请注意,反引号(和$())“返回”它们运行的​​最后一个命令的错误代码。解决方案:

CRIT_ERRS=$(cat $CRON_LOG | grep "ERROR" | grep -v "Duplicate tracking code" | cat)

答案 3 :(得分:1)

将错误消息重定向到/dev/null不会对脚本返回的退出状态执行任何操作。您的ls命令未导致错误的原因是因为它是管道的一部分,管道的退出状态是其中 last 命令的返回值(除非{已启用{1}}。

鉴于您的更新,看起来失败的命令是管道中的最后一个pipefailgrep只有在找到匹配项时才会返回grep;否则返回0,如果遇到错误,则返回1。这是2的危险;即使你不指望它们,事情也会失败,因为set -e之类的命令即使没有错误也会返回非零状态。它也无法在管道中较早的错误处退出,因此可能会遗漏一些错误。

由geocar或ephemient提供的解决方案(通过grep管道或使用cat来确保管道中的最后一个命令成功返回)应该可以帮助您解决这个问题,如果您真的想要使用|| :