开发者

Why does set -e cause my script to exit when it encounters the following?

开发者 https://www.devze.com 2022-12-16 05:09 出处:网络
I have a bash script that checks some log files created by a cron job that have time stamps in the filename (down to the second). It uses the following code:

I have a bash script that checks some log files created by a cron job that have time stamps in the filename (down to the second). It uses the following code:

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}开发者_StackOverflow中文版]\n"
else
        printf "%74s[${txtred}FAIL${txtrst}]\n"
        printf "Critical errors detected! Outputting to console...\n"
        echo $CRIT_ERRS
fi

So this bit of code works fine, but I'm trying to clean up my scripts now and implement set -e at the top of all of them. When i do it to this script, it exits with error code 1. Note that I have errors form the first statement dumping to /dev/null. This is because some days the file has the word "true" and other days "false" in it. Anyway, i don't think this is my problem because the script outputs "Checking xxxxx.log for errors." before exiting when I add set -e to the top.

Note: the $CRON_DATE variable is derived form user input. I can run the exact same statement from command line "$./checkcron.sh 01/06/2010" and it works fine without the set -e statement at the top of the script.

UPDATE: I added "set -x" to my script and narrowed the problem down. The last bit of output is:

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...

So it looks like the problem is occurring on this line:

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

Any help is appreciated. :)

Thanks, Ryan


Adding set -x, which prints a trace of the script's execution, may help you diagnose the source of the error.

Edit:

Your grep is returning an exit code of 1 since it's not finding the "ERROR" string.

Edit 2:

My apologies regarding the colon. I didn't test it.

However, the following works (I tested this one before spouting off) and avoids calling the external cat. Because you're setting a variable using the results of a subshell and set -e looks at the subshell as a whole, you can do this:

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


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

Note that backticks (and $()) "return" the error code of the last command they run. Solution:

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


Redirecting error messages to /dev/null does nothing about the exit status returned by the script. The reason your ls command isn't causing the error is because it's part of a pipeline, and the exit status of the pipeline is the return value of the last command in it (unless pipefail is enabled).

Given your update, it looks like the command that's failing is the last grep in the pipeline. grep only returns 0 if it finds a match; otherwise it returns 1, and if it encounters an error, it returns 2. This is a danger of set -e; things can fail even when you don't expect them to, because commands like grep return non-zero status even if there hasn't been an error. It also fails to exit on errors earlier in a pipeline, and so may miss some error.

The solutions given by geocar or ephemient (piping through cat or using || : to ensure that the last command in the pipe returns successfully) should help you get around this, if you really want to use set -e.


Asking for set -e makes the script exit as soon as a simple command exits with a non-zero exit status. This combines perniciously with your ls command, which exits with a non-zero status when asked to list a non-existent file, which is always the case for you because the true and false variants don't co-exist.

0

精彩评论

暂无评论...
验证码 换一张
取 消