Multiple -a with greater than / less than break bash script - linux

I wrote a bash script that performs a curl call only during business hours. For some reason, the hourly comparison fails when I add an "-a" operator (and for some reason my bash does not recognize "&&").
Though the script is much larger, here is the relevant piece:
HOUR=`date +%k`
if [ $HOUR > 7 -a $HOUR < 17 ];
then
//do sync
fi
The script gives me the error:
./tracksync: (last line): Cannot open (line number): No such file
However, this comparison does not fail:
if [ $DAY != "SUNDAY" -a $HOUR > 7 ];
then
//do sync
fi
Is my syntax wrong or is this a problem with my bash?

You cannot use < and > in bash scripts as such. Use -lt and -gt for that:
if [ $HOUR -gt 7 -a $HOUR -lt 17 ]
< and > are used by the shell to perform redirection of stdin or stdout.
The comparison that you say is working is actually creating a file named 7 in the current directory.
As for &&, that also has a special meaning for the shell and is used for creating an "AND list" of commands.
The best documentation for all these: man bash (and man test for details on comparison operators)

There are a few answers here but none of them recommend actual numerical context.
Here is how to do it in bash:
if (( hour > 7 && hour < 17 )); then
...
fi
Note that "$" is not needed to expand variables in numerical context.

I suggest you use quotes around variable references and "standard" operators:
if [ "$HOUR" -gt 7 -a "$HOUR" -lt 17 ]; ...; fi

Try using [[ instead, because it is safer and has more features. Also use -gt and -lt for numeric comparison.
if [[ $HOUR -gt 7 && $HOUR -lt 17 ]]
then
# do something
fi

Related

bash: convert string to int & if int > #

I would like to have a bash script that checks if a file has more than # amount of lines but i have not yet got it working right and I'm not so sure on how to do it.
I've never used bash before.
right now i use: linesStr=$(cat log | wc -l) to get the amount of lines in the file (expect it to be a string). when echo'ing it gives me the number 30 which is correct.
but since its most likely a string it doesnt do the if-statement, so i need to have linesStr converted into a int called linesInt.
I also have the feeling the if-statement itself is not done correctly either.
#!/bin/bash
linesStr=$(cat log | wc -l)
echo $linesStr
if [$linesStr > 29]
then echo "log file is bigger than 29 lines"
#sed -i 1d log
fi
I would appreciate if anyone can give me a simple beginners solution.
No need for cat.
Lack of spaces around [ and ].
Use a numeric comparison operator instead of the redirect operator.
Here is a working script.
#!/bin/bash
linesStr=$( wc -l < log )
if [[ "$linesStr" -gt "29" ]]; then
echo Foo
fi
your if block of code is wrong if [$linesStr > 29] there should be a space after [ and before ]
#!/bin/bash
linesStr=$(wc -l < log )
echo $linesStr
if [[ $lineStr -gt 29 ]];then
echo "log file is bigger than 29 lines"
fi
it is advisable that you always use [[ ]] with an if statement rather than using [ ]. Whenever you want to compare integers dont use > or <, use -gt -ge -lt -le. And if you want to do any form of mathematical comparison it is advisable that you use (( )).
(( lineStr > 29 )) && {
# do stuff
}
you should also note that you don't need the bash comparison operators or getting the value of a variable with $ when using (( ))
There are no string or integer types to convert. The problem is that you're using the wrong comparison operator. For numeric comparison use if [ $linesStr -gt 29 ]. Read man bash section CONDITIONAL EXPRESSIONS for available operators.
(( $(wc -l < log) > 29 )) && echo too long

Variable comparison error

Hello everybody and sorry for bad English!
I'm trying to make a "telegram alert" and made this conditional:
NOW=$(date +%s)
NOWCHECK=$((NOW-3))
[...]
if ("$DATE" < "$NOWCHECK"); then # DATE is a string variable with seconds passed from 1/1/1970
...
fi
I get this error:
line 26: 1458939588: No such file or directory
What am I doing wrong?
Thanks in advance!
What you're experiencing is Bash is trying to execute the expression within (...). It's interpreted as running the $DATE command, and redirecting input to it from $NOWCHECK. But that's not what you want.
The operator for arithmetic operations is ((...)) not (...). Do like this:
if (("$DATE" < "$NOWCHECK")); then
And it would be better to drop the $ inside the ((...)):
if ((DATE < NOWCHECK)); then
Use:
if [[ "$DATE" -lt "$NOWCHECK" ]]; # -lt: less than
If you have this two variables set:
NOW=$(date +%s) NOWCHECK=$((NOW-3))
Then: Either switch to correct Arithmetic Expansion (( ... ))
if (( NOW < NOWCHECK )); then
...
fi
Or remove the < character (which is interpreted as a redirection and presents an error as the source file named as the value of NOWCHECK does not exist):
if [[ $NOW -lt $NOWCHECK ]]; then
...
fi
if [ "$NOW" -lt "$NOWCHECK" ]; then
...
fi

Unexpected output in bash shell script

For the below script I am expecting the output to be msg_y and msg_z. But it is printing msg_x and msg_z. Can somebody explain to me what is going on?
#!/bin/bash
set -x
vr=2
echo $vr
if [ $vr > 5 ]
then
echo "entered 1st if"
echo "msg_x"
echo "out of 1st if"
if [ $vr < 8 ]; then
echo "in of 2nd if"
echo "msg_y"
else
echo "msg_z"
fi
else
if [ $vr > 1 ]; then echo "msg_y"
else echo "msg_z"
fi
fi
This expression
[ $vr > 5 ]
is being parsed as an output redirection; check to see if you have a file named "5" now. The output redirection is vacuously true. Note that the usual admonition to quote parameters inside a test expression would not help here (but it's still a good idea).
You can escape the > so that it is seen as an operator in the test command:
if [ "$vr" \> 5 ]; then
or you can use the integer comparison operator -gt
if [ "$vr" -gt 5 ]; then.
Since you are using bash, you can use the more robust
conditional expression
if [[ $vr > 5 ]]; then
or
if [[ $vr -gt 5 ]]; then
or use an arithmetic expression
if (( vr > 5 )); then
to do your comparisions (likewise for the others).
Note: although I showed how to make > work as a comparison operator even when surrounded by integers, don't do this. Most of the time, you won't get the results you want, since the arguments are compared lexicographically, not numerically. Try [ 2 \> 10 ] && echo What? Either use the correct integer comparison operators (-gt et al.) or use an arithmetic expression.

Is value in range with bash shell

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 &

Compare time using date command

Say I want a certain block of a bash script executed only if it is between 8 am (8:00) and 5 pm (17:00), and do nothing otherwise. The script is running continuously.
So far I am using the date command.
How to use it to compare it to the time range?
Just check if the current hour of the day is between 8 and 5 - since you're using round numbers, you don't even have to muck around with minutes:
hour=$(date +%H)
if [ "$hour" -lt 17 -a "$hour" -ge 8 ]; then
# do stuff
fi
Of course, this is true at 8:00:00 but false at 5:00:00; hopefully that's not a big deal.
For more complex time ranges, an easier approach might be to convert back to unix time where you can compare more easily:
begin=$(date --date="8:00" +%s)
end=$(date --date="17:00" +%s)
now=$(date +%s)
# if you want to use a given time instead of the current time, use
# $(date --date=$some_time +%s)
if [ "$begin" -le "$now" -a "$now" -le "$end" ]; then
# do stuff
fi
Of course, I have made the mistake of answering the question as asked. As seamus suggests, you could just use a cron job - it could start the service at 8 and take it down at 5, or just run it at the expected times between.
Why not just use a cron job?
Otherwise
if [[ `date +%H` -ge 8 && `date +%H` -lt 17 ]];then
do_stuff()
fi
will do the job
The bash comparison (using [[x]] or ((x)) ) will error due to leading zero for date +%H:
((: 08: value too great for base (error token is "08")
However you can pipe it through bc to remove the zero:
H=`date +%H | bc` # remove leading zero
if (( $H >= 8 )) && (( $H < 17 )); then
do_something_amazing()
fi

Resources