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.
Related
#!/bin/bash
clear
echo "Wishing according to Time of the PC"
h=$(date '+%H') #This line works, I've Checked.
if test $h -gt 6 -a $h -lt 12
then
echo "Good Morning"
elif test $h -gt 12 -a $h -lt 16
then
echo "Good Afternoon"
elif test $h -gt 16 -a $h -lt 20
then
echo "Good Evening"
else
echo "Good Night"
fi
Output should be coming according to My PC's time which is 4:00 PM but the above code isn't executing that if and elif conditions (I mean, if and elif are always returning false) and is directly jumping to else part. I am using Linux 7.9. I tried using nested if as well but it returns false always as well.
you're not capturing when $h is 16
only when it's less or more
see -ge (greater than or equal to) in the below
allow me to make this more idiomatic (use of test is not current practice, use [[ ... ]]):
h=$(date '+%H')
h=${h#0} # remove leading zero
if [[ $h -gt 6 && $h -lt 12 ]]
then
echo "Good Morning"
elif [[ $h -ge 12 && $h -lt 16 ]]
then
echo "Good Afternoon"
elif [[ $h -ge 16 && $h -lt 20 ]]
then
echo "Good Evening"
else
echo "Good Night"
fi
note that I remove any leading zero,
this is because a leading zero can make a number (e.g. 09)
interpreted as an octal, which is not what you want!
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
For example, in the below script startover starts back from the top:
##########################################################################
## CHECK TIME
##########################################################################
time=$(date +%k%M)
if [[ "$time" -ge 1800 ]] && [[ "$time" -le 2200 ]];then
echo "Not a good time to transcode video!" && exit 0
else
echo "Excellent time to transcode video!" && echo "Lets get started!"
fi
##########################################################################
## CHECK TIME
##########################################################################
startover
Also keeping in mind exit 0 should be able to stop the script.
You could "recurse" using the following line:
exec bash "$0" "$#"
Since $0 is the path to the current script, this line starts the script without creating a new process, meaning you don't need to worry about too many restarts overflowing the process table on your machine.
Put it in a while loop. I'd also suggest you add a "sleep" so that you're not racing your machine's CPU as fast as it will go:
while true; do
##########################################################################
## CHECK TIME
##########################################################################
time=$(date +%k%M)
if [[ "$time" -ge 1800 ]] && [[ "$time" -le 2200 ]]; then
echo "Not a good time to transcode video!" && exit 0
else
echo "Excellent time to transcode video!" && echo "Lets get started!"
fi
##########################################################################
## CHECK TIME
##########################################################################
for i in {1..5}; do
echo $i
sleep 1
done
done
DO NOT USE WHILE LOOP at the start of the script since the condition below will exit the script and break the loop.
echo "Not a good time to transcode video!" && exit 0
You can try trapping the exit signal so that when the script exits it restarts
##########################################################################
## CHECK TIME
############bash##############################################################
trap '<path to script> ' EXIT
time=$(date +%k%M)
if [[ "$time" -ge 1800 ]] && [[ "$time" -le 2200 ]];then
echo "Not a good time to transcode video!" && exit 0
sleep 1;
else
echo "Excellent time to transcode video!" && echo "Lets get started!"
sleep 1;
fi
##########################################################################
## CHECK TIME
##########################################################################
echo 1
echo 2
echo 3
echo 4
echo 5
startover
Note: I add a sleep of 1 second because this will give you the time to see message. trap the exit signal and re-running the script is acting like a while loop. I am also assuming that these codes are in a script.
How about enclosing the entire script in a while loop? For example,
while :
do
script
done
You may want to add a condition to break out of the loop.
This is not good practice, but what you asked for.
Put this at the end of your script. "$( cd "$( dirname "$0" )" && pwd )/$(basename $0)"
I have a simple script in Bash to read a number in a file and then compare it with a different threshold. The output is this:
: integer expression expected
: integer expression expected
OK: 3
My code is this:
#!/bin/bash
wget=$(wget http://10.228.28.8/ -O /tmp/wget.txt 2>/dev/null)
output=$(cat /tmp/wget.txt | awk 'NR==6')
#output=7
echo $output
if [ $output -ge 11 ];then
echo "CRITICAL: $output"
exit 2
elif [ $output -ge 6 ] && [ $output -lt 11 ];then
echo "WARNING: $output"
exit 1
else
echo "OK: $output"
exit 0
fi
rm /tmp/wget.txt
I know what is the problem, I know that I'm reading a string and I try to compare a int. But I don't know how can I do to read this file and convert the number to read in a int var..
Any ideas?
The problem occurs when $output is the empty string; whether or not you quote the expansion (and you should), you'll get the integer expression required error. You need to handle the empty string explictly, with a default value of zero (or whatever default makes sense).
wget=$(wget http://10.228.28.8/ -O /tmp/wget.txt 2>/dev/null)
output=$(awk 'NR==6' < /tmp/get.txt)
output=${output:-0}
if [ "$output" -ge 11 ];then
echo "CRITICAL: $output"
exit 2
elif [ "$output" -ge 6 ];then
echo "WARNING: $output"
exit 1
else
echo "OK: $output"
exit 0
fi
(If you reach the elif, you already know the value of $output is less than 11; there's no need to check again.)
The problem also occurs, and is consistent with the error message, if output ends with a carriage return. You can remove that with
output=${output%$'\r'}
There are a couple of suggestions from my side regarding your code.
You could explicitly tell bash the output is an integer
declare -i output # See [1]
Change
output=$(cat /tmp/wget.txt | awk 'NR==6') # See [2]
may be better written as
output=$(awk 'NR==6' /tmp/wget.txt )
Change
if [ $output -ge 11 ]
to
if [ "0$output" -ge 11 ] # See [4]
or
if (( output >= 11 )) # Better See [3]
References
Check bash [ declare ].
Useless use of cat. Check [ this ]
Quoting [ this ] answer :
((...)) enable you to omit the dollar signs on integer and array variables and include spaces around operators for readability. Also empty variable automatically defaults to 0 in such a statement.
The zero in the beginning of "0$output" help you deal with empty $output
Interesting
Useless use of cat is a phrase that has been resounding in SO for long. Check [ this ]
[ #chepner ] has dealt with the empty output fiasco using [ bash parameter expansion ] in his [ answer ], worth having a look at.
A simplified script:
#!/bin/bash
wget=$(wget http://10.228.28.8/ -O /tmp/wget.txt 2>/dev/null)
output=$(awk 'NR==6' </tmp/wget.txt )
output="$(( 10#${output//[^0-9]} + 0 ))"
(( output >= 11 )) && { echo "CRITICAL: $output"; exit 2; }
(( output >= 6 )) && { echo "WARNING: $output"; exit 1; }
echo "OK: $output"
The key line to cleanup any input is:
output="$(( 10#${output//[^0-9]} + 0 ))"
${output//[^0-9]} Will leave only digits from 0 to 9 (will remove all non-numeric chars).
10#${output//[^0-9]} Will convert output to a base 10 number.
That will correctly convert numbers like 0019
"$(( 10#${output//[^0-9]} + 0 ))" Will produce a zero for a missing value.
Then the resulting number stored in output will be compared to limits and the corresponding output will be printed.
In BASH, It is a good idea to use double brackets for strings:
if [[ testing strings ]]; then
<whatever>
else
<whatever>
fi
Or double parenthesis for integers:
if (( testing ints )); then
<whatever>
else
<whatever>
fi
For example try this:
var1="foo bar"
if [ $var1 == 'foo bar' ]; then
echo "ok"
fi
Result:
$ bash: [: too many arguments
Now, this:
var2="foo bar"
if [[ $a == "foo bar" ]]; then
echo "ok"
fi
Result:
ok
For that, your code in BASH:
if [[ $output -ge 11 ]]; then
echo "CRITICAL: $output"
exit 2
elif [[ $output -ge 6 ]]; then
echo "WARNING: $output"
exit 1
else
echo "OK: $output"
exit 0
fi
In bash shell, how can be value checked if within range by most effective way?
Example:
now=`date +%H%M`
if [ $now -ge 2245 ] && [ $now -le 2345 ] ; then
...
fi
...this one is working, but with using now variable.
Other option is:
if [ $((`date +%H%M`)) -ge 2245 ] && [ $((`date +%H%M`)) -le 2345 ] ; then
...
fi
...without variable, but with execution of date twice.
How to do it with one date execution and no variable at all?
First off, as a general rule, I'm pretty sure you need EITHER to use a variable OR run the command twice to do multiple comparisons on arbitrary numbers. There is no such notation as if [ 1000 -lt $(date '+%H%M') -lt 2000 ];.
Also, you don't need to put your backquoted commands inside $((...)). The result of the backquoted command is a string which /bin/[ will be interpreted by -gt or -le as a number.
if [ `date '+%H%M'` -gt 2245 -a `date '+%H%M'` -lt 2345 ]; then
That said, as an option for the times in your example, you can try using a smarter date command line.
In FreeBSD:
if [ `date -v-45M '+%H'` -eq 22 ]; then
Or in Linux:
if [ `date -d '45 minutes ago' '+%H'` -eq 22 ]; then
You can use Shell Arithmetic to make your code clear.
now=`date +%H%M`
if ((2245<now && now<2345)); then
....
fi
I would write:
if ( now=$(date +%H%M) ; ! [[ $now < 2245 ]] && ! [[ $now > 2345 ]] ) ; then
...
fi
which is mostly equivalent to your first example, but restricts the $now variable to a subshell (...), so at least it doesn't pollute your variable-space or risk overwriting an existing variable.
It also (thanks to shellter's comment) avoids the problem of $now being interpreted as an octal number when %H%M is (for example) 0900. (It avoids this problem by using string comparison instead of integer comparison. Another way to avoid this problem would be to prefix all values with a literal 1, adding 10,000 to each of them.)
#!/bin/bash
while :
do
MAX_TIME="1845"
MIN_TIME="1545"
if ( now=$(date +%H%M) ; ! [[ $now < $MIN_TIME ]] && ! [[ $now > $MAX_TIME ]] ) ;
then
echo "You are in time range and executing the commands ...!"
else
echo "Maximum Time is $MAX_TIME ...!"
# echo "Current Time is $now .....!"
echo "Minimum Time is $MIN_TIME ....!"
fi
sleep 4
done
#nohup sh /root/Date_Comp.sh > /dev/null 2>&1 &