代码之家  ›  专栏  ›  技术社区  ›  SDGuero

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

  •  2
  • SDGuero  · 技术社区  · 16 年前

    我有一个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时出错。这是因为有些日子文件中有“真”和其他日子的“假”。无论如何,我不认为这是我的问题,因为在我将set-e添加到顶部退出之前,脚本输出“checking xxxxx.log for errors”。

    注意:$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 回复  |  直到 16 年前
        1
  •  1
  •   Brian Campbell Dennis Williamson    16 年前

    重定向错误 信息 /dev/null 什么都不做 退出状态 由脚本返回。你的原因 ls 命令不会导致错误,因为它是管道的一部分,并且管道的退出状态是 最后的 命令(除非 pipefail 启用)。

    考虑到您的更新,看起来失败的命令是最后一个 grep 在管道中。 格雷普 只有回报 0 如果找到匹配项,则返回 1 ,如果遇到错误,则返回 2 . 这是一种危险 set -e ;即使在您不期望的情况下,事情也会失败,因为命令 格雷普 即使没有错误,也返回非零状态。它也无法在管道的早期错误时退出,因此可能会错过一些错误。

    geocar或ephemient给出的解决方案(通过管道 cat 或使用 || : 为了确保管道中的最后一个命令成功返回),如果您真的想使用 集合E .

        2
  •  3
  •   Dennis Williamson    16 年前

    正在添加 set -x 它打印脚本执行的跟踪信息,可以帮助您诊断错误的来源。

    编辑:

    您的grep返回的退出代码是1,因为它找不到“错误”字符串。

    编辑2:

    我为结肠问题道歉。我没有测试它。

    但是,下面的工作(我在喷口之前测试过这个)并避免调用外部 cat . 因为你用子shell的结果设置了一个变量, set -e 从整个子shell来看,可以这样做:

    CRIT_ERRS=$(cat $CRON_LOG | grep "ERROR" | grep -v "Duplicate tracking code"; true)
    
        3
  •  2
  •   Hyman Rosen    16 年前

    请求 set -e 使脚本在简单命令以非零退出状态退出时立即退出。这与你的 ls 命令,当被要求列出一个不存在的文件时,该命令以非零状态退出,这对于您来说总是如此,因为 true false 变体不共存。

        4
  •  2
  •   geocar    16 年前
    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)