Cron expression of every 3 minutes between specific time - cron

Here is the scenario:-
I want to run a specific function in my NodeJs application and for that i am using NodeScheduler
I know i can use this expression
*/3 8-15 * * *
for every 3 minutes between 8 AM to 3 PM but i want to run it between 8:30 AM to 3:15 PM but Cron expression for this which i made is certainly wrong
30-15/3 8-15 * * *
does anyone know what can be correct cron expression for this scenario ?

The normal cron doesn't give you that level of expressiveness but there's nothing stopping you from putting further restrictions in the command portion of the entry:
*/3 8-15 * * * [[ 1$(date +\%H\%M) -ge 10830 ]] && [[ 1$(date +\%H\%M) -le 11515 ]] && payload
This will actually run the cron job itself every three minutes between 8am and 4pm, but the payload (your script that does the actual work) will only be called if the current time is between 8:30am and 3:15pm.
The placing of 1 in front of the time is simply a trick to avoid issues treating numbers starting with zero as octal.
In fact, I have a script withinTime.sh that proves useful for this sort of thing:
usage() {
[[ -n "$1" ]] && echo "$1"
echo "Usage: withinTime <hhmmStart> <hhmmEnd>"
}
validTime() {
[[ "$1" =~ ^[0-9]{4}$ ]] || return 1 # Ensure 0000-9999.
[[ $1 -le 2359 ]] || return 1 # Ensure 0000-2359.
return 0
}
# Parameter checking.
[[ $# -ne 2 ]] && { usage "ERROR: Not enough parameters"; exit 1; }
validTime "$1" || { usage "ERROR: invalid hhmmStart '$1'"; exit 1; }
validTime "$2" || { usage "ERROR: invalid hhmmEnd '$2'"; exit 1; }
now="1$(date +%H%M)"
[[ ${now} -lt 1${1} ]] && exit 1 # If before start.
[[ ${now} -gt 1${2} ]] && exit 1 # If after end.
# Within range, flag okay.
exit 0
With this script in your path, you can simplify the cron command a little:
*/3 8-15 * * * /home/pax/bin/withinTime.sh 0830 1515 && payload

Related

Stop a loop process in shell scripting after some time

I have a script that looks like this, this script is checking whether my pods is in Running state or Not by redefining x on every loop.
x=$(/opt/oc get pods --selector app=${bamboo.shortPlanName} -o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')
until [ "$x" == "Running" ];
do
sleep 5
x=$(/opt/oc get pods --selector app=${bamboo.shortPlanName} -o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')
echo $x
done
But, I want to modify my current script to also support timeout, I mean after 60 second of looping, then it should be stop, or after 12 times of looping, then it should be stop. Any idea how to do that?
bash provides a special variable SECONDS that can be used as a rough timer.
SECONDS=0
while (( SECONDS < 60)); do
x=$(/opt/oc get pods --selector app=${bamboo.shortPlanName} -o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')
if [[ $x == Running ]]; then
break
fi
sleep 5
done
The expansion of SECONDS gives you not the assigned value, but the difference between the numbers of seconds since the assignment and the assigned value. The effect is like a variable whose value is incremented by 1 each second.
For timeout after 60 seconds try this Shellcheck-clean code:
#! /bin/bash -p
readonly kOC_SLEEP_SECS=5
readonly kOC_TIMEOUT_SECS=60
initial_secs=$SECONDS
while
status=$(/opt/oc get pods --selector app=bamboo.shortPlanName \
-o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')
printf '%s\n' "$status"
[[ $status != Running ]]
do
if (( (SECONDS - initial_secs) >= kOC_TIMEOUT_SECS )); then
echo 'ERROR: Timed out' >&2
exit 1
fi
sleep -- "$kOC_SLEEP_SECS"
done
I replaced app=${bamboo.shortPlanName} with app=bamboo.shortPlanName because the old code was causing Bash errors. You'll need to fix it properly.
See Why is printf better than echo? for an explanation of why I replaced echo with printf for printing the status.
The code treats a timeout as an error, and exits with bad status. You might want to do something different.
The actual timeout period will be somewhere between 60 and 65 (or maybe a little more) seconds. You'll need to do something different if you need a more accurate timeout.
For a timeout after 12 iterations try this Shellcheck-clean variation on the code above:
#! /bin/bash -p
readonly kOC_SLEEP_SECS=5
readonly kOC_MAX_ITERS=12
oc_iters=0
while
status=$(/opt/oc get pods --selector app=bamboo.shortPlanName \
-o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')
printf '%s\n' "$status"
[[ $status != Running ]]
do
if (( ++oc_iters >= kOC_MAX_ITERS )); then
echo 'ERROR: Timed out' >&2
exit 1
fi
sleep -- "$kOC_SLEEP_SECS"
done
I think trap is the easiest way to have an accurate timeout.
Prototype:
#! /bin/bash
HALT=0
TIMEOUT=4
# Trap for SIGALRM
stop_loop() {
HALT=1
}
# Set trap
trap stop_loop SIGALRM
# The timeout goes after $TIMEOUT seconds.
{ sleep $TIMEOUT && kill -SIGALRM $$ & }
# Main loop
until false || [[ $HALT -eq 1 ]]; do
sleep 1
echo 'loop'
done
echo 'out of loop'
exit 0
In your case, this looks a something like:
HALT=0
TIMEOUT=4
stop_loop() {
HALT=1
}
trap stop_loop SIGALRM
x=$(/opt/oc get pods --selector app=${bamboo.shortPlanName} \
-o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')
{ sleep $TIMEOUT && kill -SIGALRM $$ & }
until [ "$x" == "Running" ] || [[ $HALT -eq 1 ]];
do
sleep 5
x=$(/opt/oc get pods --selector app=${bamboo.shortPlanName} \
-o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')
echo $x
done
exit 0

if command is true for X seconds run code in bash shell script

i want an if statement to only run if the condition in the statement is true for at least 3 consecutive seconds, how do i achieve it in linux bash shell script?
i haven't done it before, and couldn't find anything specific by googling and would greatly appreciate any help!
i don't mean like a loop that executes the condition x amount of times, but a function that checks of the condition is true for at least x consecutive seconds/minutes before it runs a block of code.
thanks in advance
You could define this function:
check_cond() {
local stop=$(($(date +%s)+3))
while :; do
cond && return 1
[ $(date +%s) -gt $stop ] && break
done
}
It returns 1 if cond is false within the 3 second time period. It returns 0 otherwise.
Check it once, wait 3 sec and check again, something like this
$ test=
[[ $test ]] && { sleep 3; [[ $test ]]; } && echo ok || echo fail
fail
$ test=1
$ [[ $test ]] && { sleep 3; [[ $test ]]; } && echo ok || echo fail
ok

How to record each command's duration/result and display this information with `history`?

I have some commands that take a really long time to run. I'd like to be able to keep track of all of them in history.
I've looked into history flags, but those seem to only show the time/date the command started and the status code.
Partially answering your question, here is a function I have in my .zshrc to send a desktop notification when a command took more than 10 s.
You may adapt to build your own history file.
(To have the notifications, you must have notify-send or adapt $notifier)
notifier=notify-send
if [[ ${TERM[1,5]} == xterm ]]; then
preexec() {
notifiable_cmd=$1
notifiable_start_time=`date +%s`
}
precmd() {
if (( $? == 0 )); then
notifiable_status="Success in "
else
notifiable_status="Failed in "
fi
if [[ "$notifiable_cmd" != "" ]]; then
(( notifiable_time = `date +%s` - $notifiable_start_time ))
if [[ $notifiable_time -ge 60 ]]; then
notifiable_str_time="$(($notifiable_time%100000/60)) min $(($notifiable_time%60)) s"
else
notifiable_str_time="$notifiable_time[1,5] s"
fi
if [[ $notifiable_time -gt 10 ]]; then
$notifier $notifiable_cmd "$notifiable_status $notifiable_str_time"
fi
fi
notifiable_cmd=
}
fi
(Adapted from this article Growl upon job completion in zsh on contrapunctus.net)

How to run command inside bash script only during specified period of time?

Let say, I need "command" to be run only if current time is from 11:10am till 2:30pm. How this can be done in bash script?
Something like below written in pseudo-language:
#!/bin/bash
while(1) {
if ((currentTime > 11:10am) && (currentTime <2:30pm)) {
run command;
sleep 10;
}
}
The other answers overlook that when a number starts with 0, Bash will interprete it in radix 8†. So, e.g., when it's 9am, date '+%H%M' will return 0900 which is an invalid number in Bash. (not anymore).
A proper and safe solution, using modern Bash:
while :; do
current=$(date '+%H%M') || exit 1 # or whatever error handle
(( current=(10#$current) )) # force bash to consider current in radix 10
(( current > 1110 && current < 1430 )) && run command # || error_handle
sleep 10
done
Could be shortened a bit if you accept a potential 10s delay for the first run:
while sleep 10; do
current=$(date '+%H%M') || exit 1 # or whatever error handle
(( current=(10#$current) )) # force bash to consider current in radix 10
(( current > 1110 && current < 1430 )) && run command # || error_handle
done
Done!
† Look:
$ current=0900
$ if [[ $current -gt 1000 ]]; then echo "does it work?"; fi
bash: [[: 0900: value too great for base (error token is "0900")
$ # oooops
$ (( current=(10#$current) ))
$ echo "$current"
900
$ # good :)
As xsc points out in a comment, it works with the ancient [ builtin... but that's a thing of the past :).
You could try something like :
currentTime=$(date "+%H%M")
if [ "$currentTime" -gt "1110" -a "$currentTime" -lt "1430" ]; then
# ...
fi
# ...
Or :
currentTime=$(date "+%H%M")
if [ "$currentTime" -gt "1110" ] && [ $currentTime -lt "1430" ]; then
# ...
fi
# ...
Or :
currentTime=$(date "+%H%M")
[ "$currentTime" -gt "1110" ] && [ "$currentTime" -lt "1430" ] && {
# ...
}
# ...
See man date for more details. You can also use a cron job to do more than run this script from 11:30.
NB : for your loop, you could use something like :
while [ 1 ]; do
#...
done
Or :
while (( 1 )); do
#...
done
You can create a 4-digit number describing the current time with date +"%H%M". I think that could be used to compare against other times (in 24h-format) numerically:
while [ 1 ]; do
currentTime=$(date +"%H%M");
if [ "$currentTime" -gt 1110 ] && [ "$currentTime" -lt 1430 ]; then
...
fi
sleep 10; # probably better to have this outside the if-statement
done
If you want to handle a timespan that includes midnight you just have to replace the && with ||.

bash script for cron task minutely

I have a Paas cron service that only run on minutely, hourly, daily, weekly. I want my script to run o every 15 minutes at 8am, 9am, 10am, 11am, 4pm and 5pm. how do I do that. Here is my not working bash script.
#!/bin/bash
MINUTES=`date +%M`
HOUR=`date +%H`
for j in "08" "09" "10" "11" "13" "16" "17";do
if [ "$HOUR" == "$j" ];then
for i in "00" "15" "30" "45";do
if [ "$MINUTES" == "$i" ];then
(
/usr/bin/notify-send "Script is running at "
)
fi
done
fi
done
Why don't just use the features of crontab?
Add this line to the crontab
*/15 8,9,10,11,16,17 * * * /path/bin/yourscript.sh
I'd be looking at something like (run by cron every minute):
#!/usr/bin/bash
# Exit if hour not valid.
hh=$(date +%H) ; hhn=${hh##0}
if [[ $hhn -lt 8 ]] ; then exit ; fi
if [[ $hhn -gt 17 ]] ; then exit ; fi
if [[ $hhn -eq 12 ]] ; then exit ; fi
if [[ $hhn -eq 14 ]] ; then exit ; fi
if [[ $hhn -eq 15 ]] ; then exit ; fi
# Exit if not multiple of 15 minutes.
mm=$(date +%M) ; mmn=${mm##0} ; ((mm15 = mmn % 15))
if [[ $mm15 -ne 0 ]] ; then exit ; fi
# Otherwise do something.
echo Doing something at $hh:$mm
This has the advantage of not doing a lot of loops to check whether or not you should run.
The ${hh##0} construct simply removes any leading zero from the hour so 08 and 09 can be used in arithmetic comparisons without being treated as invalid octal (ditto for the minute).
All of the if statements simply exit prematurely from the script if the hour and/or minute should prevent the run. The minute one is simpler since it's a modulo-15 calculation.

Resources