commit a698d517dd0d4884e282231c4b416d29e9b58c10 from: Alexander Arkhipov date: Fri Jun 7 06:26:24 2024 UTC add the cron tricks article commit - 350689b3a1c52d34b0a2e0a280aace6a299f99e5 commit + a698d517dd0d4884e282231c4b416d29e9b58c10 blob - /dev/null blob + 02bf9a81625ec14d06b0bb7b7fae3eee771f7cd7 (mode 644) --- /dev/null +++ art/015.cron_tricks.txt @@ -0,0 +1,164 @@ +Title: Cron tricks +Author: Alexander Arkhipov +Created: 2024-06-06 +Modified: 2024-06-06 + +cron(8) is one of Unix' most infamous, most helpful, and most annoying +daemons. There are lots of tricks, which can help make the experience so +less painful. Let's review some of these! + + +GENERAL + + Getting your environment right + +A nice trick to set your environment, as if you were in a login shell, +is to create the following file (say, ~/.shrc): + +eval `/bin/ksh -l -c 'export -p'` + +And then load it like so: + +. ~/.shrc + +Of course, if you are using ksh, you may just do something like + +. ~/.profile; . ~/.kshrc + +But the first method is, I think, more general, as you only need to +modify one word in one file if you change your shell, etc. In fact, cron +sets HOME, SHELL and USER, so you should even be able to do this: + +eval `$SHELL -l -c 'export -p'` + +But I wouldn't go as far. + +To then to use it with cron, create a file like so (say, ~/bin/cjob): + +#!/bin/sh + +. $HOME/.shrc + +case $1 in +ajob) ajob_command ;; +bjob) bjob_command ;; +esac + +And make it executable with `chmod 755 ~/bin/cjob`. + +The cronjobs themselves would look something like so: + +*/30 * * * * $HOME/bin/cjob ajob +0 8 * * * $HOME/bin/cjob bjob >/dev/null + + + Using command output with %-syntax + +It is well-known that % has special meaning in crontab: The first one +specifies that everything after should go on the commands stdin, and the +ones after that are replaced with a newline. This second property makes +them very useful, since crontab doesn't allow newlines otherwise. +However, this does not provide for cases when we want to use command +output. This doesn't work, for example: + +0 12 * * * mail -s hey bob%hello bob, it's $(date +\%H:\%M) + +You can instead employ a trick with cat: + +*/10 */2 * * * { cat; date +\%H:\%M; } | mail -s hey bob%hello, bob, it's + +(There's a single space at the end.) + +Unfortunately that only permits command substitution at the beginning, +and at the end. For anything more complicated, you might want to use +echo(1) and printf(1). + + + More general approach to newlines + +You can use printf(1) to get newlines in your commands, though you'll +have to escape the backslash twice: + +*/10 */2 * * * printf '%s\\n%s' "hey bob" "it's $(date +%H:%M)" | mail -s hey bob + +Or if you want to have a special character standing for newline, (e.g. +@) you can do that with tr, or sed: + +*/10 */2 * * * echo "hey bob@it's $(date +%H:%M)@Made With tr(1)" | tr @ \\\\n | mail -s hey bob +*/10 */2 * * * echo "hey bob@it's $(date +%H:%M)@Made With sed(1)" | sed s/@/\\\\n/g | mail -s hey bob + +(Note the doubly-escaped backslashes in all of the above!) + + + Doing things with tmux + +Most tmux commands are good enough at assuming the first session they +can get, but let's also check for any sessions existing first: + +0 12 * * * tmux ls >/dev/null 2>&1 && tmux popup echo "it's 12 o'clock" + + +OPENBSD SPECIFIC + +There are many implementations of cron. Most have extensions. Here are +some OpenBSD specific: + +- Random intervals with ~. E.g., beep at random minute every hour: + +~ * * * * printf \\\\b + +- Command output is mailed to the crontab owner! Really awesome for + debugging, and noticing problems early. Copious mail output is not a + problem either because of... +- Flags! Commands can be run with flags -nqs, like so: + +# no e-mail will be sent because of the -n flag +* * * * * -n echo hello +# in spite of -n, mail will be sent due to non-zero exit status +* * * * * -n echo bad; false + +- "Steps" (slash-syntax, like */2), are an OpenBSD extension, though as + far as I know it's found in most modern implementations. + + + X11 programs + +You might know that this works: + +0 12 * * * DISPLAY=:0 /usr/X11R6/bin/xmessage "it's 12 o'clock!" + +(Though even that may require messing with XAUTHORITY, to which I can't +give you any good advice.) + +Finding a more general approach to setting DISPLAY is ironically very +system-specific. On OpenBSD you can use who(1): + +0 12 * * * DISPLAY=`who | awk -vu=$USER '$1 == u && $NF ~ /^[(]:[0-9]+[)]$/ {print substr($NF, 2, length($NF)-2); exit}'` /usr/X11R6/bin/xmessage "it's 12 o'clock" + +(I put parantheses into brackets here to avoid doubly-escaping +the backslashes.) + +If you have many regularly-running X11 cronjobs, you can save the +who(1)-filtering bit in a script (e.g. ~/bin/getdisp): + +#!/bin/sh + +who | awk -v u=${1:-$USER} ' + $1 == u && $NF ~ /^\(:[0-9]+\)$/ { + print substr($NF, 2, length($NF-2) + exit + } +' + +Then the cronjob becomes like so: + +0 12 * * * DISPLAY=`$HOME/bin/getdisp` /usr/X11R6/bin/xmessage "it's 12 o'clock" + +Of course, the script above can be expanded in many ways. Use your +imagination. + + +MORE? + +Do you have more cron tricks? Please write me an e-mail, and I'll add +them to the article, noting your contribution.