Reputation: 391
If I have 3 different scripts to run at various times each time a file is written to, how would a bash script be written to run a specific script only at a specific time. This is not as simple as a cron job (though cron could probably swap out the .sh file based on time), I am looking for the time variables.
For instance:
If 9am-11:30 am run scriptA.sh if file.txt is changed.
If 11:30am-5:45pm run scriptB.sh if file is changed.
If 5:46pm-8:59am run scriptC.sh if file is changed.
I asked a similar question but I don't think I was clear enough about the variables I am seeking or how to define them..
Upvotes: 1
Views: 4470
Reputation: 189317
The traditional tool for comparing time stamps to determine whether work needs to be performed or not is make
. Its default behavior is to calculate a dependency chain for the specified target(s) and determine whether any of the dependent files have changed; if not, the target does not need to be remade. This is a great tool for avoiding recompilation, but it easily extends to other tasks.
In concrete terms, you'd create a flag file (say, .made
) and specify it as dependent on your file
. Now, if file
has changed, .made
needs to be recreated, and so make
will run the commands you specify to do so. In this scenario, we would run a simple piece of shell script, then touch .made
to communicate the latest (successful) run time to future runs.
What remains is for the recipe to run different commands at different times. My approach to that would be a simple case
statement. Notice that make
interprets dollar signs, so we need to double those dollar signs which should be passed through to the shell.
.made: file
case $$(date +%H:%M) in \
09:* | 10:* | 11:[0-2]? ) \
scriptA.sh ;; \
11:[3-5]? | 1[2-6]:* | 17:[0-3]? | 17:4[0-5]) \
scriptB.sh;; \
17:4[6-9] | 17:5? | 1[89]:* | 2?:* | 0[0-8]:* ) \
scriptC.sh;; \
esac
touch $@ # $@ expands to the current target
The entire case
statement needs to be passed as a single logical line to the shell, so we end up with those pesky backslashes to escape the newlines.
Also notice that make
is picky about indentation; each (logical) line in the recipe should be preceded by a literal tab character.
The default behavior of make
is to run the first target in the file; this Makefile
only contains one target, so make
is equivalent to make .made
.
Also notice that make
cares about exit codes; if scriptA
, scriptB
, or scriptC
could exit with a non-zero exit status, that is regarded by make
as a fatal error, and the .made
file will not be updated. (You can easily guard against this, though.)
Upvotes: 3
Reputation: 3801
Well, since Bash variables can store string
or integer
only,
"date" variables are just a string manipulations, like example below:
hours=`date +%H`
minutes=`date +%M`
sum=$(($hours + $minutes))
digit=`echo $sum | sed -e 's/\(^.*\)\(.$\)/\2/'` # counts total digits
Upvotes: 0
Reputation: 2063
I see there are two issues. 1, how to determine if the current hour is within a particular range. 2, how to determine if a file has been modified, recently.
Here's how I would approach that:
#!/bin/bash
now=$( date +%s )
current_hour=$( date +%H )
file_mod=$( ls -l --time-style=+%s file.txt | awk '{print $(NF-1)}' )
file_age=$(( $now - $file_mod ))
if [[ $current_hour -gt 9 ]] && \
[[ $current_hour -lt 11 ]] && \
[[ $file_age -lt 3600 ]]
then
./scriptA.sh
fi
Here I'm using the bash operators for numeric comparison: -gt, -lt
For greater granularity with time, you would need to compute the amount of time since midnight. Perhaps something like:
hour=$( date +%H )
min=$( date +%M )
minutes=$( echo "( 60 * $hour ) + $min" | bc -l )
eleven_thirty=$( echo "( 60 * 11 ) + 30" | bc -l )
Upvotes: 2