10

I've got a strange problem with $RANDOM in cron. I wish to execute a command a random number of minutes after the cronjob fires.

This example works directly in the terminal and delays the command up to 30 seconds (replace command with whatever you want, it's actually an echo to /dev/ttyUSB0):

sleep `expr $RANDOM \% 30` ; command

If the same line is placed in crontab, the command always fires immediately without the delay:

* * * * * sleep `expr $RANDOM \% 30` ; command

If I use an expression without $RANDOM, it works fine - this makes a delay of 15 secs:

* * * * * sleep `expr 10 + 5` ; command

In other words, it seems like $RANDOM does not work in a cron.

But it is not simply because $RANDOM itself evaluates to zero, because then this should give a delay of 10:

* * * * * sleep `expr $RANDOM \% 30 + 10` ; command

I have also tried with && instread of ; but that doesn't help. In fact, then the command does not fire at all!

I could of course place the delay in a script which is then called from crontab, but that does not explain my problem and does not make me learn :-)

It's Debian Lenny if that makes any difference.

2 Answers 2

19

cron uses the /bin/sh shell to execute tasks. In some distros, this is a symlink to dash. Neither of them supports the $RANDOM variable, which is a bash-specific extension.

  • With vixie-cron, you can put a line SHELL=/bin/bash at the top of your crontab.

  • Otherwise, you will have to settle with bash -c 'echo $RANDOM' or perl -e 'print int(rand(65535))'.

    (In the above example, 65535 is the maximum number to return. You can apply other math inside the script too.)

  • In a properly configured system, you would have been informed about this by cron itself – it always sends job output, including error messages, by email. Install a lightweight MTA.


Also, in bash, $(( )) is preferred over `expr`.

8
  • Last I checked, /bin/sh wasn't an actual shell, merely a symlink to the preferred shell of the sysadmin (typically bash or dash) on Debian.
    – Hello71
    Mar 31, 2011 at 0:48
  • 1
    @Hello71: Which is what I said in the post. Nevertheless, it is customary for system software to invoke /bin/sh (and expect it to be compatible with Bourne shell). An example is the system() function in glibc. Therefore /bin/sh usually points to the fastest Bourne-compatible shell; and the sysadmin is supposed to set his preference in the appropriate line of /etc/passwd, not to enforce that preference system-wide Mar 31, 2011 at 4:39
  • @Hello71: ...okay, mostly what I said in the post. (I know most other distros link sh to bash, but it didn't seem relevant.) Mar 31, 2011 at 12:54
  • You are right, /bin/sh points to dash. Until now, I never heard about dash. I looked it up and it is a lightweight variant of bash. Also, I didn't know that cron ran in a "crippled" environment, but it explains various other problems I have had in the past. Btw, I started out using $(()) but since it didn't work I tried all sorts of variations and ended up with expr - which of course didn't work either. But that is where I ended :-) Is it possible to run a normal bash shell without the limitations using bash -c 'xxxx' ? Btw, is it not possible to put line breaks in comments?
    – marlar
    Mar 31, 2011 at 18:31
  • @marlar: 1) dash is a shell. It's not any more or less normal than bash. It's not a variant of it either. 2) See points #1 and #2 in the answer. Mar 31, 2011 at 18:34
2

cron typically runs with a less "full" environment, meaning that you simply don't have many of the same environment variables available to you. Apparently $RANDOM is one such, and in fact your sleep command is simply failing with an error because of the undefined variable -- which is why your command failed to run at all when you switched to && instead of ;. (Well, actually, $RANDOM is a Bash function, but cron doesn't run in a full Bash environment, which evidently lacks this function.)

To accomplish this task, you will need to use a separate Bash script, as you said. Alternatively, you might be able to figure out a way to use cat /dev/urandom directly in the cron command, but it would probably be easier just to move what you currently have to a separate Bash script.

2
  • 1
    It is not pretty, but I found this solution which is analog to your suggestion: sleep $(expr od -An -N1 -i /dev/urandom \% 30) ; command
    – marlar
    Mar 31, 2011 at 18:21
  • 1
    $RANDOM is not part of some "full" environment. It has nothing to do with environment variables set at a process start. It is a special variable created "on the fly" in bash. Its new value is always generated whenever the variable is read. --- cron by default uses /bin/sh so on systems where /bin/sh is not linked to bash $RANDOM will not work by default. Jun 18, 2015 at 8:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .