If condition with 2 different values within certain range (boundary)
Let's say I have a coordinate (x,y), I want to make two categories: good or bad.
I have no idea how to put it into words or keywords, so I created an example of what I want.
I have created a bash script like this:
x=3.5 #(example)
y=-2.5 #(example)
if [ $x -ge 0.1 -a $x -le 5.5 ] && [ $y -ge -5.9 -a $y -le -0.1 ]; then cat="good";
elif [ $x -ge 5.5 -a $x -le 10.5 ] && [ $y -ge -10.9 -a $y -le -5.9 ]; then cat="bad";
fi
echo "$cat"
On that script I want to know if x is within range 0.1 till 5.5 (0.1 < x < 5.5) and y within -5.9 till -0.1 (-5.9 < y < -0.1) are true then it gives result as good
. With different boundaries it should give a bad
result.
I've run it and it gives the result as integer expression expected.
When I try to replace the operator -ge
(or -le
) with operator >
(or <
), or try to add a square bracket and anything I could think of, it gives the same error or no results are given.
How should I approach this?
Solution 1:
Bash can only do integer math. If the values have a fixed number of decimal places, you can remove the .
(using sed
or awk
or any command you like) and compare the resulting values. For example, if they have exactly one decimal place like 10.0, 0.4, 2.2
, removing the .
will multiply them by ten, resulting in 100, 04, 22
(using the old test command [
, leading 0
s don't matter for integer comparison). You'll have to multiply the boundaries accordingly, of course.
Alternatively, you can use bc
, which will work with arbitrary precision floating point numbers. bc
reads stdin
, so you can, for example, echo the expression and pipe it to bc
as follows:
echo "$x>=0.1 && $x<=5.5" | bc
The output will be 1
if the expression is true
, 0
otherwise. Read the manual (man bc) if you want to learn more about its syntax.
To incorporate this in your script, you can use command substitution like this:
if [ $(echo "$x>=0.1 && $x<=5.5" | bc) -eq 1 ] ; then cat="good";
Note that we still need to check if the bc
command's output is 1
, because [ 0 ]
will also evaluate to true
.
Finally, your example using bc
:
x=6.5 #(example)
y=-7.5 #(example)
if [ $(echo "$x>=0.1 && $x<=5.5 && $y>=-5.9 && $y<=-0.1" | bc) -eq 1 ] ; then cat="good";
elif [ $(echo "$x>=5.5 && $x<=10.5 && $y>=-10.9 && $y<=-5.9" | bc) -eq 1 ]; then cat="bad";
fi
echo "$cat"
Since having those hardly readable strings inside the brackets makes it more error prone, you can use bash functions and variables like in the following example. This may be useful if you want to add additional elif
clauses with different ranges. (Thanks to wjandrea for the useful hints)
x=6.5 #(example)
y=-6.5 #(example)
boundsGood="0.1 5.5 -5.9 -0.1"
boundsBad="5.5 10.5 -10.9 -5.9"
# Paramaters in following order: x, y, xmin, xmax, ymin, ymax; bounds are inclusive.
function in_bounds {
local x=$1
local y=$2
local x_min=$3
local x_max=$4
local y_min=$5
local y_max=$6
[ $(echo "$x >= $x_min && $x <= $x_max && $y >= $y_min && $y <= $y_max" | bc) -eq 1 ]
}
if in_bounds $x $y $boundsGood ; then cat="good";
elif in_bounds $x $y $boundsBad ; then cat="bad";
fi
echo "$cat"
Alternative version of the in_bounds
function using printf
(thanks to steeldriver):
# Paramaters in following order: x, y, xmin, xmax, ymin, ymax; bounds are inclusive.
function in_bounds {
[ $(printf "x = %f; y = %f; xmin = %f; xmax = %f; ymin = %f; ymax = %f; x >= xmin && x <= xmax && y >= ymin && y <= ymax\n" "$1" "$2" "$3" "$4" "$5" "$6" | bc) -eq 1 ]
}
Solution 2:
If it's absolutely necessary to remain within shell script - prepend variables to awk
embedded script to pass the x
and y
values into awk's environment and use ENVIRON["variable_name"]
to access them; and let it do the job of printing the strings you need (or wrap the whole script into $(...)
to save the output).
If it's not necessary, do away with shell script part, and just let awk alone handle everything.
#!/bin/sh
x=3.5
y=-2.5
x="$x" y="$y" awk 'BEGIN{
print ENVIRON["x"],ENVIRON["y"];
if ((ENVIRON["x"] >= 0.1) && (ENVIRON["x"] <= 5.5) \
&& (ENVIRON["y"] >= -5.9) && (ENVIRON["y"] <= -0.1)){
print "good";
}
else if ((ENVIRON["x"] >= 5.5) && (ENVIRON["x"] <= 10.5) \
&& (ENVIRON["y"] >= -10.9) && (ENVIRON["y"] <= -5.9)){
print "bad";
}
}' # End of awk