cron
is great. Using cron
, you can schedule commands to be run at regular intervals or at specific times. One of the major drawbacks of cron
is that it doesn't generally keep state regarding job's status. If a job is scheduled to run at midnight, but the system is powered down at midnight, the command will never be run.There are a few solutions out there to take care of this shortcomings, but many are designed for system-wide use only. What if you don't want to intermingle your personal jobs with the system-wide ones? Here is a fairly simple script that works as a wrapper, providing a little insurance to help make sure jobs get run.
#!/bin/bash
export P_ENV=~/.profile
export P_TRACK=~/.p_track
if [ -e "$P_ENV" ]; then
. $P_ENV
fi
case "$1" in
h)
export P_NOW=$(date +%Y-%m-%d-%H)
;;
d)
export P_NOW=$(date +%Y-%m-%d)
;;
w)
export P_NOW=$(date +%Y-%U)
;;
m)
export P_NOW=$(date +%Y-%m)
;;
*)
echo ERROR: Term not specified. Must be one of h, d, w, m .
exit 1
esac
if [ -z "$2" ]; then
echo ERROR: Command to run not specified.
exit 2
fi
export P_TAG=$(echo $2 | sed -e 's/[^A-Za-z0-9]/-/g')
export P_FILE=$P_TRACK/$P_TAG-$P_NOW
if [ -e "$P_FILE" ]; then
exit 0
else
rm -f $P_TRACK/$P_TAG*
echo Executing $2 at $(date)
$2 $3 $4 $5 $6 $7 $8 $9
echo Done
if [ "$?" -eq "0" ]; then
touch $P_FILE
fi
fi
The script has 4 operating modes. It can help ensure command are run hourly, daily, weekly, or monthly. It uses empty files in a configurable directory (
~/.p_track
by default) to keep tabs on the last time the command was run. Entries in the tracking directory are only created if the command run returns exit status 0 (no error.)To use this wrapper, place it and a period (
h
for hourly, d
for daily, and so on) in front of commands in your crontab like so:*/20 * * * * ~/bin/periodic h ~/bin/command param1 param2
The above will check to see if the command needs to be run every 20 minutes, but will only execute it every hour. The advantage of wrapping individual commands instead using periodic command directories (check out the
run-parts
command) is less administrative overhead.Note: The "real"
periodic
command is generally used to run system-wide periodic commands, often stored in /etc/periodic
. UNIX-y systems tend to use other mechanisms and directories to do something similar.Update 1: Changed the script from a group of
if/elif
to case
(thanks for pointing that out Dave) and added a quick import of .profile
or another file to set up environment.
4 comments:
For many modern crons, I find it sufficent to run important jobs with the @reboot option. For example:
## Normal run
0 * * * * /bin/command param1 param2
## In case computer was off
@reboot /bin/command param1 param2
This way, if my command failed to run while the computer was off, it will be run as soon as the computer is restarted. That might mean the command will be run more than once per hour, but its easy to see how long the computer was off with the following command:
last -x shutdown
A small script can check the final field outputed by the command above and see if its longer than N amount of time, and if so, return true, but if not, return false.
Then:
@reboot off-time 1h && /bin/command ...
-Dave
That is probably good for most user, but I tend to go from 2 days to 2 weeks without a reboot. Thankfully, there is no shortage of options in this area of the sysadmin's universe.
True. Shell scripts rock.
Do you know about the bourne shell's case builtin? I think you could rewrite the long if,elif stanza like the following:
case "$1" in
h)
export P_NOW=$(date +%Y-%m-%d-%h)
;;
d)
...
;;
....
*)
echo "Error: command to run not specified. Dying"
exit 2
esac
(Blogger kills the whitespace I tried to insert. Sorry)
-Dave
Yes, although 95% of the time I never use case in shell scripts? Why? No idea.
Post a Comment