As this little-known ‘feature’ of cron has now bitten me several times I thought I should write a note about it both so I’m more likely to remember in future, but also so that other people can learn about it. I remember a few years ago when I was working for Webfusion we had some cronjobs to maintain the databases and had some error message that kept popping up that we wanted to remove periodically. We set up a command looking something like:
0 * * * * mysql ... -e 'delete from log where message like "error to remove%"'
but it was not executing. Following on from that, today I had some code to automatically create snapshots of a certain btrfs filesystem (however I recommend that for serious snapshotting you use the excellent (if a bit hard to use) snapper tool):
0 5 * * 0 root /sbin/btrfs subvol snap -r /home/ /home/.snapshots/$(date +%Y-%m-%d)
But it was not executing… Looking at the syslog output we see that cron is running a truncated version of it:
May 14 05:00:02 localhost /USR/SBIN/CRON: (root) CMD (/sbin/btrfs subvol snap -r /home/ /home/.snapshots/$(date +)
Looking in the crontab manual we see:
Percent-signs (%) in the command, unless escaped with backslash (\), will be changed into newline characters, and all data after the first % will be sent to the command as standard input.
D’oh. Fortunately the fix is simple:
0 5 * * 0 root /sbin/btrfs subvol snap -r /home/ /home/.snapshots/$(date +\%Y-\%m-\%d)
I’m yet to meet anyone who is using this feature to pipe data into a process run from crontab. I’m also yet to meet even very experienced sysadmins who have noticed this behaviour making this a pretty good interview question for a know-it-all sysadmin candidate!