Wolin Labs :: fun toys for your iThingy & Droid

Articles

Don't Lose your Garmin GPS!

Cron Job with Random Start Delay

Git Submodule with Local Changes Example

Using Caltopo Maps on your Garmin GPS

Waffle Reuben

Copying a Single File with a Yocto Recipe

Semihosting Debugging on STM32F4

Realtime Audio DSP with the STM32F4

Tuning the Moog Etherwave Theremin

STM32 Discovery Development on Linux

STM32F4 USB Virtual COM Port (VCP)

Editing GoPro Hero2 Video with Cinelerra

Code Browsing with Emacs, GLOBAL, and Speedbar

Use a PC Power Supply as a Bench Supply the Easy Way

Android Activity Bar Framework

Generating an Audio Sine Wave with Java

Android Simple Yes/No MessageBox

Fox Talas 32 Oil/Seal Change Checklist

Creating a Bootloader Environment (Freescale ColdFire Example)

Pentomino Smackdown: Names That Didn't Make the Cut

Cron Job with Random Start Delay

Ross Wolin - last updated 2016.07.12



The Problem


I have some cron jobs on a Raspberry Pi running Raspbian I'd like to run each evening at 11pm with a random start delay of up to 2hrs. Some distros' crons have support for a RANDOM_DELAY variable in the crontab file; however the man page for Raspbian Jessie doesn't mention it, and a quick test showed setting RANDOM_DELAY in crontab did nothing.

When I Google'd this problem, I also found answers that involved using the $RANDOM variable with the sleep command. Unfortunately, $RANDOM is a Bash specific feature, and as the follow-up comments pointed out since cron jobs run in the Bource (sh) shell, $RANDOM is not set, so this solution doesn't work.




My Solution


My quick and dirty solution was to write a Python script with accepts a time delay maximum, then generates the string 'sleep' followed by a random delay in seconds up to the specified maximum. The output from this command is prepended to the cron job entry using shell command substitution, with something like:

`~/bin/rndsleep.py 15m` && ~/bin/scrape.py 

where the `` indicates to run the script output as a command via command substitution

The script takes one parameter, the maximum delay in seconds, minutes, or hours:


rowol@tesla$ ./rndsleep.py --help
usage: rndsleep.py [-h] max_delay

Generate sleep command of a random duration up to max_delay

positional arguments:
  max_delay   maxiumum time in N seconds (Ns or N), minutes (Nm), or hours (Nh) to sleep

optional arguments:
  -h, --help  show this help message and exit

The script converts the delay to seconds and generates a random number between 0 and the maximum for the duration, and outputs the corresponding sleep command to STDOUT. You may ask "why not just do the sleep in the Python script?" My thought was that piping the command back to the shell via command substitution allows the Python interpreter to exit, so it's not taking up memory during the sleep.

#! /usr/bin/python
#
# Generates command for a random sleep, given a max delay in seconds, minutes, or hours
# Useful for cron, as RANDOM_DELAY doesn't work on Raspian, 
# and $RANDOM is a Bash shell thing (cron uses Bourne/sh shell)
#
# Install with 'crontab -e' to run every day at 11pm with 2 hour random start delay, 
# with a command like:
# 0 23 * * * `~/bin/rndsleep.py 2h` && ~/bin/scrape.py 
#

import argparse, random, re, sys

def main():
    random.seed()

    p = argparse.ArgumentParser(description='Generate sleep command for random amount of time')
    p.add_argument("time", help="time in N seconds (Ns or N), minutes (Nm), or hours (Nh)");
    args = p.parse_args()

    # Parse lowercase time arg against N, Ns, Nm, and Nh
    args.time = args.time.lower() 
    m = re.match("(\d+)(s?|m|h)$", args.time)
    if not m:
        sys.stderr.write("ERR - Unable to parse the time parameter (%s)\n"), % args.time)
        sys.exit(-1)

    # Grab the delay, and if it's hours or minutes, convert to seconds
    maxDelay = int(m.group(1))
    if m.group(2)=="m":
        maxDelay *= 60
    elif m.group(2)=="h":
        maxDelay *= 60*60

    # This goes between the ``
    print "sleep %d" % random.randint(0, maxDelay),
    sys.exit()



if __name__ == "__main__":
    main()



Send comments, questions, money in large denominations, etc to eng at mysticengineering.com



If you enjoyed this article, please consider buying my products ...

ATX PS Adapter Ultimate Serial Port
ATX PS Adapter

Use an ATX PC power supply as a 5V, 3.3V, and +12V/-12V bench supply the easy way, without cutting the case or mounting external connectors, resistors, LEDs, switches, and fuses.

Provides visual indication when supply is plugged in and turned on, also fuses the power voltage outputs for safety. Run USB powered development boards via the USB connectors on the 5V line.

Ultimate Serial Port (Debug Buddy)

USB serial port with standard, 5V and 3V RS232, plus integrated null modem and gender changer. Implements TX/RX and RTS#/CTS# for optional hardware handshake.

Also includes 3.3V<->5V level shifters, debug LEDs, and 13 clock sources. Valuable tool for hands on problem solving and hacking