We may want to run some jobs for every two weeks/months/days… under some situation such as backing up for every other week. In addition, we may add more complex rules for running jobs, e.g. run a command when the load of the server is higher than a certain level. With the help of the shell programming language we can easily achieve this goal.
Basics of the crontab ∞
Crontab file controls how and what to run by cron. The crontab line has a format as follows:
.---------------- minute (0-59) | .------------- hour (0-23) | | .---------- day of month (1-31) | | | .------- month (1-12) OR jan,feb,mar,apr ... | | | | .---- day of week (0-6) (Sunday=0 or 7) OR sun,mon,tue ... | | | | | * * * * * command to be executed
To add a cron job for your current user, run
$ crontab -e
And edit the crontab file by adding/deleting lines. Check the crontab man page for more details.
The cron job is run as a piece of shell code.
/bin/sh is the shell by default. You may change it to some other shells like bash by adding a like like
SHELL=/bin/bash at the beginning of the crontab file.
Method 1: use crontab extensions ∞
crontab provides some extensions to support this: ranges can include “steps” like
1-9/2 is the same as
1,3,5,7,9. This can be the easiest method if the job is intended to be run every two N in a range such as N is “day” and the range is “days in a month”. Check the crontab man page for more details about ranges and steps of crontab.
To run the command every two months at 1:00 on the first day of the month, the crontab is
0 1 1 */2 * command to be executed
But please be aware that the every two N here is not exactly every two N. For days, it is every two days in a month. For months, it is every two months in a year. If you need to make sure your cron job is run exactly every two N, check the other methods.
Method 2: use a test statement to control whether to really run the command ∞
The command executed for a cron job is a piece of shell code. So you can make the command run every day/week/month while use a test statement to control whether to really run the command.
Let’s take an example to illustrate the idea. Now, we want to run a job every 2 days. As there are months having 31 days, some jobs are not run exactly every 2 days if we use method 1 above. We can have a cron job as follows to make sure it runs exactly every 2 days (86400 seconds) at 1:00.
0 1 * * * [[ $((($(date +%s) / 86400) % 2)) == 0 ]] && /path/to/your/command
Here, the shell script
[[ $((($(date +%s) / 86400) % 2)) == 0 ]] && /path/to/your/command is run every day. But your command is run only when
$((($(date +%s) / 86400) % 2)) == 0. That is when the number of days since 1970-01-01 00:00:00 UTC is divisible by 2.
With the help of
date, we can make make test statements. Let’s take another example, run a job exactly every 5 months starting from March of 2016. The cron tab entry can be
0 1 1 * * [[ $(((($(date +%Y) - 2016) * 12 + $(date +%m) - 3) % 5)) == 0 ]] && /path/to/your/command
Only when the number of months since March 2016 is divisible by 5, the command is run.
Method 3: use a shell script with its state saved on disk ∞
The idea is to invoke a shell script every day/week/month and the shell judges whether to run the job. The key idea here is that the shell uses a file to mark its state.
The shell code:
#!/bin/bash # a file marking the state on disk mark_file=$HOME/.job-run-marker-1 # check whether the job run last time it is invoked if [ -e $mark_file ] ; then rm -f $mark_file else touch $mark_file exit 0 fi # job command is here
The script will not find $mark_file on the first run, so it will create it and exit. On the second run the script will remove $mark_file and then proceed to execute the job command. For the third run, it is the same as the first run. So if this script is run weekly by cron, the job command will run every two weeks.
You can extend the shell with more complex logic like storing the last run date/time in the $mark_file or check more system information like the CPU/mem/disk load.
An example is as follows.
We want to back up Xen DomU VMs for every two weeks. We create a script /home/share/bin/xen-bak. The beginning part of this script is like what we list above.
The line for the job is as follows.
0 2 * * 2 /home/share/bin/xen-bak
The backup command will run at 2:00 for every other Tuesday.