Skip to content

Instantly share code, notes, and snippets.

@coderofsalvation
Last active July 23, 2024 16:22
Show Gist options
  • Save coderofsalvation/1102e56d3d4dcbb1e36f to your computer and use it in GitHub Desktop.
Save coderofsalvation/1102e56d3d4dcbb1e36f to your computer and use it in GitHub Desktop.
cronjob wrapper with locking support
# portable cronjob wrapper to ensure :
#
# * only one process at the time (prevents process-overlap, handy for unique cron workers)
# * reverse cron's email behaviour (only emails on error)
# * ultraportable: only relies on flock, not on debians 'start-stop-daemon' or centos 'daemon'
# * nicelevel to tame cpu usage
#
# usage: cronjoblock <application> [args]
# example: cronjoblock /home/foo/myscript &
# cronjoblock /home/foo/myscript & <--- 'myscript' will only run when the previous run is finished
#
# there's is no output unless there are errors. This is handy in respect to cron's MAILTO variable:
# the stdout/stderr output will be suppressed (so cron will not send mails) *unless* the given process
# has an exitcode > 0
#
[[ ! -n $1 ]] && { head -n11 $0 | sed 's/^#/ /g' | grep -v 'bin/bash'; exit; }
EXITCODE=0
STDOUT="/tmp/.cronjoblock.$( echo "$*" | tr A-Z a-z | sed -e 's/[^a-zA-Z0-9\-]/-/g')" # logfilename containing cronjob args
LOCKFILE="$STDOUT.lock"
NICELEVEL=10 # don't overload cpu (higher is slower)
exec nice -n $NICELEVEL /usr/bin/flock -w 0 "$LOCKFILE" "$@" | tee -a "$STDOUT" # remove -a to flush log on each run
EXITCODE=$$; [[ ! $EXITCODE == 0 ]] && cat "$STDOUT" # output stdout to cron *only* on error
exit $EXITCODE # let cron know (trigger email)
SHELL=/bin/bash
MAILTO="[email protected]"
# -------------- min (0 - 59)
# | --------------- hour (0 - 23)
# | | ---------------- day of month (1 - 31)
# | | | ----------------- month (1 - 12)
# | | | | ------------------ day of week (0 - 6) (0 to 6 are Sunday to Saturday, or use names; 7 is Sunday, the same as 0)
# | | | | |
# | | | | |
# * * * * * command to execute
*/5 * * * * cd /my/path && ./cronjoblock foo bar # run every 5 mins if not running
*/5 * * * * cd /my/path && ./cronjoblock foo bar &> /dev/null # run every 5 mins if not running (no output)
@coderofsalvation
Copy link
Author

coderofsalvation commented Dec 5, 2023

Hi, thanks for making this script, I wanted to know if you could make a variation that only locks the cronjob to 1 process at the time (like the original), but still outputs everything to the console, so I can log everything into a file using the tee command, thanks!

done, I've added tee, so it'll always log to a file + stdout (and you can always tee afterwards) :D
(or add &>/dev/null to supress stdout+stderr, see example)

Thanks for that script. Can you please add more comments to your code. It is always hard to understand bash. I am not sure what happens here.

done, I've annotated it.

Keep on cronning! ❤

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment