Execute bash script literally after every 3 days

To immediately abort and exit a script if the last execution was not yet at least a specific time ago, you could use this method which requires an external file that stores the last execution date and time.

Add these lines to the top of your Bash script:

#!/bin/bash

# File that stores the last execution date in plain text:
datefile=/path/to/your/datefile

# Minimum delay between two script executions, in seconds. 
seconds=$((60*60*24*3))

# Test if datefile exists and compare the difference between the stored date 
# and now with the given minimum delay in seconds. 
# Exit with error code 1 if the minimum delay is not exceeded yet.
if test -f "$datefile" ; then
    if test "$(($(date "+%s")-$(date -f "$datefile" "+%s")))" -lt "$seconds" ; then
        echo "This script may not yet be started again."
        exit 1
    fi
fi

# Store the current date and time in datefile
date -R > "$datefile"

# Insert your normal script here:

Don't forget to set a meaningful value as datefile= and adapt the value of seconds= to your needs ($((60*60*24*3)) evaluates to 3 days).


If you don't want a separate file, you could also store the last execution time in the modification time stamp of your script. That means however that making any changes to your script file will reset the 3 counter and be treated like if the script was successfully running.

To implement that, add the snippet below to the top of your script file:

#!/bin/bash

# Minimum delay between two script executions, in seconds. 
seconds=$((60*60*24*3))

# Compare the difference between this script's modification time stamp 
# and the current date with the given minimum delay in seconds. 
# Exit with error code 1 if the minimum delay is not exceeded yet.
if test "$(($(date "+%s")-$(date -r "$0" "+%s")))" -lt "$seconds" ; then
    echo "This script may not yet be started again."
    exit 1
fi

# Store the current date as modification time stamp of this script file
touch -m -- "$0"

# Insert your normal script here:

Again, don't forget to adapt the value of seconds= to your needs ($((60*60*24*3)) evaluates to 3 days).


Cron really is the wrong tool for this. There's actually a commonly used and underloved tool called at which might work. at's mainly designed for interactive use and I'm sure that someone would find a better way to do this.

In my case I'd have the script I am running listed in testjobs.txt, and include a line that reads.

As an example, I would have this as testjobs.txt

echo "cat" >> foo.txt
date >> foo.txt
at now + 3 days < testjobs.txt

I have two innocent commands, which might be your shellscripts. I run echo to make sure I have a deterministic output, and date to confirm the command runs as needed. When at runs this command, it will finish by adding a new job to at for 3 days. (I tested with one minute - which works)

I'm pretty sure I'll be called out for the manner I've abused at, but its a handy tool for scheduling a command to be run at a time or x days after a previous command.


First, the code fragment above is invalid Bash syntax, looks like Perl. Second, the z parameter to date causes it to output the numeric time zone. +%j is the day number. You need:

if [[ ! $(( $(date +%j) % 3 )) ]] ;then
     exit
fi

But you're still going to see strangeness at year-end:

$ for i in 364 365  1 ; do echo "Day $i, $(( $i % 3 ))"; done
Day 364, 1
Day 365, 2
Day 1, 1

You might have better luck with keeping a count in a file, and testing/updating that.