Have you ever had a cronjob that needs to run as root, but you can’t do so easily because of the following reasons?
- /etc/crontab is managed by your package manager
- vixie-cron ignores /etc/cron.d/
- Root’s private “
crontab -e
” crontab is easily overlooked when backing up /etc - It needs some odd interval like “every 3 days”
Well, if your interval is evenly divisible into days, (“every 2 days” is OK, “every 1.5 days” is not) then I have just the solution. Create a new cron script in /etc/cron.daily
, make it executable, and put the following inside it (For our example, I’ll use two days as the interval and “emerge –sync” as the command):
#!/bin/bash
TODAY=`date +%j`
if [ $((${TODAY#0} % 2)) == 0 ]; then
/usr/bin/emerge --sync > /dev/null
fi
It’s that simple. Here’s how it works:
- Once per day, the cron daemon calls the script
- The script calls `date +%j` and gets the “day of the year” (between 1 and 365, inclusive, or 1 and 366 if it’s a leap year)
- It then strips off any leading zeros to prevent “invalid number for given base” errors. (“085” would be “invalid octal” by bash syntax.)
- The script does a “modulo 2” operation on it (takes the remainder of integer division by 2)
- If there is no remainder, then the current date satisfies the “every second day” requirement and the command is run.
Because I chose the evenly-numbered days using “== 0”, a non-leap year will end with a two-day interval, rather than a leap year ending with the command being called two days in a row. Use “!= 0” if you want to change this behaviour.
The “> /dev/null” ensures that only stderr messages (usually only errors) generate status emails. I use that to keep my admin mailbox uncluttered.
UPDATE: Oops. I forgot that bash is one of the many languages which uses a zero prefix to indicate octal. I’ve corrected the example.
Crontab Timing Tricks by Stephan Sokolow is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
Stephan,
Thanks for the tip! I also had a strange interval — every 29 days. I ended up using a slightly different technique to get the number of days:
let upSeconds=`cat /proc/uptime | cut -f1 -d.`
let days=$((${upSeconds}/86400))
After that, my solution was about the same as yours (i.e. check if days % 29 equals 0).