72

How can I store variables in my crontab? I realize it's not shell but say I want to have some constants like a path to my app or something?

JP Silvashy
  • 46,977
  • 48
  • 149
  • 227

5 Answers5

88

In Vixie cron, which is possibly the most common, you can do this almost exactly like a shell script.

VARIABLE=value
PATH=/bin:/path/to/doathing
0 0 * * * doathing.sh $VARIABLE

The man page says:

An active line in a crontab will be either an environment setting or a cron command. An environment setting is of the form,

     name = value

where the spaces around the equal-sign (=) are optional, and any subsequent non-leading spaces in value will be part of the value assigned to name. The value string may be placed in quotes (single or double, but matching) to preserve leading or trailing blanks. The name string may also be placed in quote (single or double, but matching) to preserve leading, trailing or inner blanks.

You can tell if you have Vixie cron by checking the man page for crontab; the author will be Paul Vixie. Different crons may or may not support this (BusyBox's cron for example, does not), in which case your best option is to wrap your command in a shell script and run that script from cron instead. In fact, this is a good thing to do for anything complicated.

Matt K
  • 13,370
  • 2
  • 32
  • 51
  • 5
    Came back to look at this question I asked 3 years ago. Funny how things go huh? – JP Silvashy Jul 01 '13 at 08:57
  • 47
    This post is misleading - cron only supports a specific set of variables being set in the cron table; e.g. HOME, MAILTO, SHELL, etc. You *cannot* set custom variables, e.g. FOO=/foo/dir. I figured I would post this since this post lead me down the wrong road for a couple hours. – Jmoney38 Jul 08 '13 at 11:50
  • @Jmoney38: on what system and what version of cron? What does your crontab look like? The version of cron that ships with OS X has this explicitly stated as supported in the man page. – Matt K Jul 11 '13 at 17:24
  • 1
    @Jmoney38: I just tested this with an arbitrary environment variable with Vixie cron, and it worked. I'd like to know what version of cron you are using, and what you were trying to do. – Matt K Jul 18 '13 at 02:09
  • Same here - arbitrary variables cannot be set: wasted an inordinate amount of time figuring out this puzzle. FWIW I'm using some sort of 'custom' distribution (QNAP TS-212 NAS) the closest I can get to a version is via `cat /proc/version` (no `lsb_release`) and this is what I get: `Linux version 2.6.33.2 (root@NasX86-14) (gcc version 4.2.1) #1 Fri Jul 26 11:32:35 CST 2013`. Hope this helps others save time. – Marco Massenzio Mar 19 '14 at 03:37
  • @Marco Thank you. Can you tell what version of cron is part of this distribution? – Matt K Mar 19 '14 at 14:17
  • @Marco Try `man 5 crontab`. Every cron I can find supports this. – Matt K Mar 19 '14 at 14:34
  • 2
    @mkb LoL - they did not bother adding `man` to the distro (and I do know that most others do support this) and, puzzingly, `which cron` shows nothing. `crontab -h` shows this: `BusyBox v1.01 (2013.06.23-18:42+0000) multi-call binary` - whatever that means. – Marco Massenzio Mar 21 '14 at 06:03
  • @Marco `crond` itself is probably in `/usr/sbin` or something and not usually in your PATH. Thanks, that's very helpful. – Matt K Mar 21 '14 at 21:00
  • @mkb as mentioned, this distro is a minimalist one (lives in a ramfs RAM disk) and does not have `man` installed at all. And I don't think they're even using crond (but haven’t' really checked that one yet) – Marco Massenzio Apr 04 '14 at 01:31
  • 1
    @Marco I did. They do. Here's the source. http://git.busybox.net/busybox/tree/miscutils/crond.c – Matt K Apr 04 '14 at 14:16
  • 2
    Just an observation: crontab is accepting any symbol I care to define; however it is interesting that the definitions are global within the crontab. The order in which the definitions are mixed with the cron commands does not seem to matter. – Dale Wilson Sep 17 '14 at 21:35
  • 38
    FWIW I'm using Vixie's cron, and I can define any variable I want. However, the value for these variables is **not interpolated** with other variables value, regardless of order. For example: `FOO=${HOME}/foo` doesn't work as you would expect (it leaves `"${HOME}"` verbatim / un-interpolated). – Pierre D Mar 25 '15 at 18:58
  • 1
    So I needed a date, "YD=date -d yesterday %Y-%m-%d" - and it was giving me a really strange error message, so I thought to myself, "How could the crontab syntax suck even more?", so I tried escaping the dash before the "d", like so: "YD=date \-d yesterday %Y-%m-%d" - instead of the percentage, which needs escaping everywhere else. Guess what. Crontab sucked in exactly the right way this time. Mentioning it here because info on how crontab works seems to belong in comments on completely different crontab questions. – Teekin Apr 12 '18 at 23:40
  • @Jmoney38 You can use variables [this way](https://serverfault.com/a/339822/407820) (option 3) – Pablo Bianchi Aug 12 '21 at 07:14
17

To keep my crontab clean, I would just call a shell script and do the fun stuff in the script.

Justaman
  • 320
  • 2
  • 5
  • Could you add an example of how to do that? – rer Oct 03 '18 at 17:56
  • ```vi ~/scripts/fun_stuff.sh chmod 700 ~/scripts/fun_stuff.sh crontab -e``` fun_stuff.sh can be any shell script you like (e.g.: ```#!/bin/bash a="Green Vegetables" b="are tasty." m="$a $b ... NOT" echo "$m"``` ). Then, add an entry to crontab to run it on schedule. Hmm. Doesn't like my line breaks. Oh well. – Jack Simth Dec 21 '20 at 18:04
  • After all this time, I needed to update this to the correct answer, which is clearly this. The other answer that I had originally marked as correct, if it can be done, really should not be done. – JP Silvashy Jan 09 '21 at 17:13
8

I think the important fact to point out here is (as stated in an earlier comment by Pierre D Mar 25, 2015 at 18:58) that variable declarations are not expand/interpolated and so can not embed other variable values.

Variables are only expanded/interpolated in the commands themselves.

So:

var1 = bar
var2 = foo${var1}
42 17 * * * /path/to/command ${var2}

Results in: /path/to/command foo${var1}

While:

var1 = bar
var2 = foo
42 17 * * * /path/to/command ${var2}${var1}

Results in: /path/to/command foobar

So in my case the following works fine, no wrapping in shell scripts required:

SHELL=/bin/bash
timestamp=date +20%y_%m_%d_%H_%M_%S
logdir=/my/log/dir

0 2 * * * /my/command/path/mycmd param >> ${logdir}/myfile_$(${timestamp}).log

verses something like this which does not work:

logfile = /my/log/dir/myfile_${timestamp}.log

since the later is not expanded, but is rather interpreted as is including "${" and "}" as part of the string.

brookbot
  • 398
  • 1
  • 3
  • 11
  • How did you get ${timestamp} to do this? In my case it just literally outputs `date | sed 's/ /_/g'` as that is standard cron behavior. – misantroop May 05 '22 at 02:35
  • I changed mine to: `DATE=date +20%y_%m_%d_%H_%M_%S` `* * * * * /bin/echo $(${DATE}) >> "${logdir}/btbuild_$(${DATE}).log" ` and have updated my answer above – brookbot May 06 '22 at 03:17
  • I went with cron calling ssh where using vars is more simplified. – misantroop May 06 '22 at 03:36
4

Just a working example of using variables in the crontab file and their substitution in the strings:

CURRENT_TIME=date +%Y.%m.%d_%H:%M:%S.%3N
CURRENT_DATE=date +%Y_%m_%d

SIMPLE_VAR=the_simple_var
LOG_DIR=/var/log/cron

* * * * * /bin/echo "simple variable test! ${SIMPLE_VAR}__test!" >> "${LOG_DIR}/test.log"
* * * * * /bin/echo "complex variable test! $(${CURRENT_TIME})__test!" >> "${LOG_DIR}/test.log"

Tested on this Docker image (paste the above crontab to the crontab.txt):

FROM debian:10-slim

# Install docker (Yep, this is a docker in docker):
RUN curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh

# Install CRON:
RUN apt-get update && apt-get install -y --no-install-recommends cron

# Add a crontab_config.txt task:
COPY crontab.txt /var/crontab.txt
RUN crontab /var/crontab.txt

ENTRYPOINT ["cron", "-f"]

Add this to the crontab to run any commands inside another docker containers:

/usr/bin/docker exec container_name ls -la
Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
James Bond
  • 2,229
  • 1
  • 15
  • 26
  • 1
    I think the important fact to point out here is that "variables [are] not interpolated with other variable values []. For example: FOO=${HOME}/foo doesn't work as you would expect (it leaves "${HOME}" verbatim / un-interpolated). – Pierre D Mar 25, 2015 at 18:58 They are only interpolated in the commands themselves. – brookbot Apr 07 '22 at 20:04
3

If you have a few environment variables you want to set for a particular job, just inline those into the sh snippet.

42 17 * * * myvariable='value' path/to/command

In case it's not obvious, the sh syntax var=value command sets var to value for the duration of command. You can have several of these if you need more than one.

42 17 * * * firstname='Fred` lastname='Muggs' path/to/command

If you have nontrivial variables you want to access from several places, probably put them in a separate file, and source (.) that file from your shell startup script and your cron jobs.

Let's say you have a file $HOME/bin/myenv with settings like

myvar=$(output of some complex command)
another='another
complex
variable'

then you can add

. $HOME/bin/myenv

to your .profile (or .zshrc or .bash_profile or what have you; but .profile is portable, and also used by sh) and

42 17 * * * . $HOME/bin/myenv; path/to/command

in your crontab.

Notice the lone dot before the space before the file name; this is the dot command (also known as source in e.g. Bash) which reads the file into the current shell instance as if you had typed in the things in the file here.

Tangentially, the $HOME/ part is strictly speaking redundant; cron jobs will always run in the invoking user's home directory.

Obviously, if you want a variable to be set in your entire crontab, set it at the top, before the scheduled jobs.

tripleee
  • 175,061
  • 34
  • 275
  • 318