为什么set -e会导致脚本在遇到以下情况时退出?

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

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 

所以这段代码工作正常,但我正在尝试清理我的脚本,并实现设置-e在所有的顶部。 当我这样做到这个脚本,它退出与错误代码1.请注意,我有错误的forms倾倒到/ dev / null的第一个语句。 这是因为有些日子文件中有“真”这个词,其他的日子是“假”的。 无论如何,我不认为这是我的问题,因为脚本输出“检查xxxxx.log的错误”。 在退出时,我将-e添加到顶部。

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

更新:我添加了“设置-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") 

任何帮助表示赞赏。 🙂

谢谢,瑞安

将错误消息重定向到/dev/null对脚本返回的退出状态没有任何作用。 你的ls命令不会导致错误的原因是因为它是流水线的一部分,流水线的退出状态是最后一条命令的返回值(除非启用了pipefail )。

鉴于你的更新,看起来像失败的命令是管道中的最后一个grep 。 如果找到一个匹配, grep只返回0 ; 否则返回1 ,如果遇到错误,则返回2 。 这是set -e的危险; 即使你不指望它们,也可能会失败,因为像grep这样的命令即使没有错误也会返回非零状态。 它也不能在一个流水线中早一些出错,所以可能会错过一些错误。

geocar或ephemient给出的解决方案(通过cat管道或使用|| :确保管道中的最后一个命令成功返回)应该可以帮助你解决这个问题,如果你真的想使用set -e

添加set -x (打印脚本执行的跟踪)可以帮助您诊断错误的来源。

编辑:

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

编辑2:

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

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

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

询问set -e使脚本尽快退出,一个简单的命令以非零退出状态退出。 当你被要求列出一个不存在的文件时,这与你的ls命令有害地结合在一起,当你被要求列出一个不存在的文件的时候,这个命令就会以非零状态退出。

 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)