views:

116

answers:

5

I have this user crontab (accessed via the command crontab -e):

# m h  dom mon dow   command
*/3 * * * *          sh /home/FRAPS/Desktop/cronCheck.sh

The script cronCheck.sh looks like that:

#!/bin/sh
SERVICE='Script'

if ps ax | grep -v grep | grep -i "$SERVICE" > /dev/null
then
    echo "######## $SERVICE service running, everything is fine ##################\n" >> CronReport.txt
else
    echo "$SERVICE is not running. Launching it now\n" >> CronReport.txt
    perl Script.pl 
fi

When I launch the script (cronCheck.sh) from its own directory, it works like a charm, but when cron launches it, it always "# $SERVICE service running, everything is fine ###" despite 'Script' is not running.

Thanks,

+1  A: 

Checking the return status of a pipe like that could be problematic. You should either check the $PIPESTATUS array, or you can pipe the final grep into wc -l to count the number of lines.

Matt Kane
+2  A: 

You need to put the grep -v grep after the grep -i "$SERVICE". The way you have it now it's guaranteed to be true.

Dennis Williamson
This is, also, astonishingly brittle (though not unusually so). For example, if some other user is running a command with 'Service' anywhere in its name your script will think everything's OK. Consider instead actually testing the service is up e.g. by trying to connect to it.
telent
+1  A: 

cron typically does not set up a lot of the environment like a user account does. You may need to modify your script to get things setup properly.

Jay
+1  A: 

Cron jobs don't get the same environment settings that you get at a shell prompt - those are generally set up by your shell on login - so you want to use absolute rather than relative paths throughout. (i.e. don't assume the PATH environment variable will exist or be set up the same as it is for you at a shell prompt, and don't assume the script will run with PWD set to your home directory, etc.) So:

  • in your crontab entry replace sh with /bin/sh (or remove it if cronCheck.sh is executable, the shebang line will do).
  • in cronCheck.sh add paths to the log file and the perl script.

cronCheck.sh should end up looking something like:

#!/bin/sh
SERVICE='Script'

if ps ax | grep -v grep | grep -i "$SERVICE" > /dev/null
then
    echo "######## $SERVICE service running, everything is fine ##################\n" >> CronReport.txt
else
    # Specify absolute path to a log file that's writeable for the user the
    # cron runs as (probably you). Example: /tmp/CronReport.txt
    echo "$SERVICE is not running. Launching it now\n" >> /tmp/CronReport.txt

    # Specify absolute path to both perl and the script. Example: /usr/bin/perl
    # and /home/FRAPS/scripts/Script.pl
    /usr/bin/perl /home/FRAPS/scripts/Script.pl 
fi

(Again you can get rid of the /usr/bin/perl bit if Script.pl is executable and has the path to the right perl in the shebang line.)

Simon Whitaker
+2  A: 

Here's an even better way to write that conditional:

services=$(ps -e -o comm | grep -cFi "$SERVICE")
case "$services" in
    (0) 
        # restart service
    ;;
    (1)
        # everything is fine
    ;;
    (*)
        # more than one copy is running
    ;;
esac

By using ps -e -o comm you avoid having to do the silly grep -v grep thing, because only the actual process name appears in the ps output, not the arguments. And grep -cFi counts up the matches and gives you a number, so you don't have to deal with the exit status of a pipeline.

Also, as other posters have implied, you should lead off this script by setting the PATH variable.

PATH=/bin:/usr/bin:/sbin:/usr/sbin
export PATH

You might or might not want to put /usr/local/bin at the beginning of that list, depending on your system. Don't do it if you don't need anything from there.

Final piece of advice: When writing scripts that will execute without user supervision (such as cron jobs), it's a good idea to put set -e at the beginning. That makes them exit unsuccessfully if any command fails.

Zack