56

I finally set up a realistic backup schedule on my data through a shell script, which are handled by cron on tight intervals. Unfortunately, I keep getting empty emails each time the CRON has been executed and not only when things go wrong.

Is it possible to only make CRON send emails when something goes wrong, ie. my TAR doesn't execute as intended?

Here's how my crontab is setup for the moment;

0 */2 * * * /bin/backup.sh 2>&1 | mail -s "Backup status" [email protected]

Thanks a lot!

6 Answers 6

71

Ideally you'd want your backup script to output nothing if everything goes as expected and only produce output when something goes wrong. Then use the MAILTO environment variable to send any output generated by your script to your email address.

[email protected]
0 */2 * * * /bin/backup.sh

If your script normally produces output but you don't care about it in cron, just sent it to /dev/null and it'll email you only when something is written to stderr.

[email protected]
0 */2 * * * /bin/backup.sh > /dev/null
4
  • 13
    This is hardly ideal. You generally want the entire output (stdout + stderr) e-mailed to you when the command ends with a non-zero error code. Otherwise, it is generally fine to gobble at least stdout. To me, this is a design flaw of cron.
    – Witiko
    Commented Jan 18, 2017 at 0:12
  • 5
    @Witiko I agree; I found this question trying to fix that. I guess you can make your cron command /bin/backup.sh > log_file || (echo Backup failed with exit status $?; cat log_file)?
    – Daniel H
    Commented Oct 17, 2017 at 14:08
  • @DanielH That can work as long as /bin/backup.sh can run exclusively.
    – robsch
    Commented Dec 10, 2020 at 18:01
  • Be careful. The MAILTO setting will apply for all following cronjobs in your crontab.
    – pascalre
    Commented Aug 8, 2023 at 14:01
36

Using cronic wrapper script looks like a good idea; to use it you don't have to change your scripts.

Instead of:

 0 1 * * * /bin/backup.sh 2>&1 | mail -s "Backup status" [email protected]

do:

 [email protected]
 0 1 * * * cronic /bin/backup.sh

Simply put; it will run silent if everything runs smooth (exit status 0), but it will report verbosely if not, and let cron handle the mail reporting.

More info on https://habilis.net/cronic/.

4
  • 1
    I really don't see how that will help when the problem is nothing more than an incorrect cron line and cron is doing exactly what it is told to do. Commented Feb 14, 2012 at 0:17
  • 4
    @JohnGardeniers it helps because sometimes you have output without an error.
    – Mikhail
    Commented Aug 31, 2014 at 19:59
  • 17
    Alternatively, chronic from the moreutils package: joeyh.name/code/moreutils Commented May 29, 2015 at 22:05
  • 1
    Not sure why everyone is upvoting chronic (vs cronic). All I could find about their difference is: Joey Hess has written a simpler perl version of cronic named chronic (cronic man page, manpages.ubuntu.com/manpages/xenial/man1/cronic.1.html). However in my case cronic worked just fine.
    – red-o-alf
    Commented Oct 24, 2022 at 10:05
13

Here is another variation that I've successfully utilized for many years - capture output and print it out only on error, triggering an email. This requires no temp files, and preserves all output. The important part is the 2>&1 that redirects STDERR to STDOUT.

Send the entire output via default cron mailer config:

1 2 * * * OUTPUT=`flexbackup -set all 2>&1` || echo "$OUTPUT"

Same but with a specific address and subject:

(address can also be changed by setting MAILTO=xxxx for the entire crontab file)

1 2 * * * OUTPUT=`flexbackup -set all 2>&1` || echo "$OUTPUT" | mail -s "Failed to backup" [email protected]

You can even perform multiple actions on error and add to email:

1 2 * * * OUTPUT=`flexbackup -set all 2>&1` || {echo "$OUTPUT" ; ls -ltr /backup/dir ; }

This will work for simple commands. If you are dealing with complex pipes (find / -type f | grep -v bla | tar something-or-other), then you're better off moving the command into a script and running the script using the aforementioned approach. The reason is that if any part of the pipe outputs to STDERR, you'll still get emails.

5

This is a very common problem, and nowadays (2021), best solved using "chronic" from the moreutils package, which is done exactly for this purpose. This package is available in most linux/bsd distribution.

chronic runs a command, and arranges for its standard out and standard error to only be displayed if the command fails (exits nonzero or crashes). If the command succeeds, any extraneous output will be hidden.

A common use for chronic is for running a cron job. Rather than trying to keep the command quiet, and having to deal with mails containing accidental output when it succeeds, and not verbose enough output when it fails, you can just run it verbosely always, and use chronic to hide the successful output.

        0 1 * * * chronic backup # instead of backup >/dev/null 2>&1
2
  • Thanks for the info. I hope it's ok that I've pasted the text here for quicker details. Commented Jan 10, 2023 at 2:16
  • 1
    Yeah, sure. Thanks for providing an example.
    – orzel
    Commented Jan 11, 2023 at 14:03
4

You should be directing the stderr anmd not both stdout and stderr.

Use 1> /dev/null not 2>&1 and it should be fine. Also, you may need to report the error correctly in your backup script.

4

You are specifically instructing cron to always send email, even when /bin/backup.sh (by the way, it should be in /usr/local/bin) succeeds. Just omit the | mail -s "Backup status" [email protected] part and email will only be sent when there is output. You can probably (depending on your cron) explicitly set the email address to mail to as an assignment in the crontab file.

For details, see

man 5 crontab

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .