3

Can I make cause cron to only send the email if the output (stderr) contains a certain string?

I'm aware of this answer but the command I run doesn't distinguish between stdout/stderr, it always just outputs to stdout, so I need to look for a string.

So far I got this and it basically works EXCEPT grep doesn't pass on the output to the mail command, so I just get an empty email:

0 5 * * * root mycommand | grep -q 'Renewal was done' && mail -s 'Renewal completed' my@email.com

How can I get the entire output from mycommand included in the email?

3 Answers3

2

I would highly suggest you put the logic into a script and run that script in cron rather than try and make a one liner script in cron. That way you can easily test it outside of cron, eg:

    #!/bin/bash
    tmp=/tmp/t$$
    mycommand > $tmp 
    if grep -q 'Renewal was done' $tmp
    then
        mail -s 'Renewal completed' my@email.com < $tmp
    fi
rm -f $tmp
exit 0

You can add checking of mycommand exit status, pass the command being run, the string being matched and the email address in as parameters etc..

Loner
  • 103
iheggie
  • 146
1

This might work.

#!/bin/sh
COMMAND=`mycommand`
FINDSTR="renewal was done"

ANSWER = `$COMMAND | grep $FINDSTR` 

if $ANSWER; then
  echo $ANSWER > mail -s 'Renewal Completed' my@email.com
fi
Jeff W.
  • 511
0

Here's how I do it:

In this scenario I have 4 databases (dumps) I need to backup and if everything went normal I want to receive an email mentioning so. If I had errors I want a message with a different subject to point it out, the failed database included in the message body and the error log attached to the email.

The first file (/root/templates/ylatis-backup.txt) is just a message body template:

Backup status report from: %hostname

Network configuration:
%net_config

Database backups:
ylatis-cy :%cy_status
ylatis-ug :%ug_status
ylatis-rw :%rw_status
ylatis-lc :%lc_status

All words prefixed by % are treated as variables and replaced by sed later on in the script.
%hostname and %net_config are used to identify which computer the email is coming from.
The variables ending in _status are replaced with the word NORMAL or ERROR so I can know what went wrong.

#!/bin/bash

NET_CONFIG=$(ifconfig |grep inet |grep -v inet6 |grep -v '127.0.0.1') MESSAGE_TEMPLATE=/root/templates/ylatis-backup.txt REPORT_FILE=/tmp/bkup-report-$$.txt MESSAGE=$(cat $MESSAGE_TEMPLATE)

MAIL_FROM='no-reply@hostname.com' MAIL_TO='user@hostname.com' MAIL_SUBJECT="[$HOSTNAME] [Backup Report: Databases] %status [$(date +%Y-%m-%d)]" MAIL_SERVER='smtp.gmail.com:587' MAIL_USER='no-reply@hostname.com' MAIL_PASS='**********'

CY_ERR=/tmp/ylatis-cy_err.$$ UG_ERR=/tmp/ylatis-ug_err.$$ RW_ERR=/tmp/ylatis-rw_err.$$ LC_ERR=/tmp/ylatis-lc_err.$$

ERROR_LOG=/tmp/error_log-$$.txt

MESSAGE=$(echo "$MESSAGE" |sed 's@%hostname@'"$HOSTNAME"'@g') MESSAGE=$(echo "$MESSAGE" |sed 's@%net_config@'"$NET_CONFIG"'@g')

mount -a rsync -azv --chown root:root lp@10.10.10.14::backup/ /mnt/hdd3/ylatis-cy/ 1>/dev/null 2>$CY_ERR cy_status=$? rsync -azv --chown root:root lp@10.10.10.10::ylatisug/ /mnt/hdd3/ylatis-ug/ 1>/dev/null 2>$UG_ERR ug_status=$? rsync -azv --chown root:root lp@10.10.10.10::ylatisrw/ /mnt/hdd3/ylatis-rw/ 1>/dev/null 2>$RW_ERR rw_status=$? rsync -azv --chown root:root lp@10.10.10.10::labco/ /mnt/hdd3/ylatis-labco/ 1>/dev/null 2>$LC_ERR lc_status=$?

if [ $cy_status -eq 0 ] && [ $ug_status -eq 0 ] && [ $rw_status -eq 0 ] && [ $lc_status -eq 0 ]; then ########## #ALL GOOD# ##########

MAIL_SUBJECT=$(echo "$MAIL_SUBJECT" |sed 's@%status@[NORMAL]@g')

MESSAGE=$(echo "$MESSAGE" |sed 's@%cy_status@NORMAL@g') MESSAGE=$(echo "$MESSAGE" |sed 's@%ug_status@NORMAL@g') MESSAGE=$(echo "$MESSAGE" |sed 's@%rw_status@NORMAL@g') MESSAGE=$(echo "$MESSAGE" |sed 's@%lc_status@NORMAL@g')

echo "$MESSAGE" >$REPORT_FILE sendemail -f $MAIL_FROM -t $MAIL_TO -u $MAIL_SUBJECT -o message- file=$REPORT_FILE -s $MAIL_SERVER -xu $MAIL_USER -xp $MAIL_PASS

else ################ #ERRORS OCCURED# ################

MAIL_SUBJECT=$(echo "$MAIL_SUBJECT" |sed 's@%status@[ERROR]@g')

if [ $cy_status -eq 0 ]; then MESSAGE=$(echo "$MESSAGE" |sed 's@%cy_status@NORMAL@g') else MESSAGE=$(echo "$MESSAGE" |sed 's@%cy_status@ERROR@g') fi

if [ $ug_status -eq 0 ]; then MESSAGE=$(echo "$MESSAGE" |sed 's@%ug_status@NORMAL@g') else MESSAGE=$(echo "$MESSAGE" |sed 's@%ug_status@ERROR@g') fi

if [ $rw_status -eq 0 ]; then MESSAGE=$(echo "$MESSAGE" |sed 's@%rw_status@NORMAL@g') else MESSAGE=$(echo "$MESSAGE" |sed 's@%rw_status@ERROR@g') fi

if [ $lc_status -eq 0 ]; then MESSAGE=$(echo "$MESSAGE" |sed 's@%lc_status@NORMAL@g') else MESSAGE=$(echo "$MESSAGE" |sed 's@%lc_status@ERROR@g') fi

echo "$MESSAGE" >$REPORT_FILE

cat /dev/null >$ERROR_LOG find /tmp/ -name *.$$ -exec cat {} ; >>$ERROR_LOG cp $ERROR_LOG . sendemail -f $MAIL_FROM -t $MAIL_TO -u $MAIL_SUBJECT -o message-file=$REPORT_FILE -a error_log-$$.txt -s $MAIL_SERVER -xu $MAIL_USER -xp $MAIL_PASS rm error_log-$$.txt

fi

#Cleanup rm /tmp/.$$ 2>/dev/null rm /tmp/$$.txt 2>/dev/null

Note how I am monitoring the exit status of rsync to determine what the message and subject of my email should be. In my case I am discarding normal output (1>/dev/null) and keeping only the standard error output (2>$error_log). You could just use the >> operator if you want to keep all the output. You could also adjust to include the log in the message instead of an attachment for convenience if you are reading your mail from terminal.

Maybe it looks like an overkill to a relatively simple question but I think it outlines well what you are trying to do. You need to build your message while running your command through an intermediate script and send it later. If you find the above code useful and have any questions on it feel free to ask in comments.