28

I am trying to compare two decimal values but I am getting errors. I used

if [ "$(echo $result1 '>' $result2 | bc -l)" -eq 1 ];then

as suggested by the other Stack Overflow thread.

I am getting errors.

What is the correct way to go about this?

Zombo
  • 1
  • 55
  • 342
  • 375

8 Answers8

43

You can do it using Bash's numeric context:

if (( $(echo "$result1 > $result2" | bc -l) )); then

bc will output 0 or 1 and the (( )) will interpret them as false or true respectively.

The same thing using AWK:

if (( $(echo "$result1 $result2" | awk '{print ($1 > $2)}') )); then
Dennis Williamson
  • 324,833
  • 88
  • 366
  • 429
  • 11
    +1 for the awk solution, but: `if echo $result1 $result2 | awk '{exit !( $1 > $2)}'; then ...` – William Pursell Jun 28 '12 at 14:19
  • I like to retract my down vote as Dennis's reply is correct with decimal points. For expanded floating point arithmetic using bash, see my reply below. – Samir Oct 17 '17 at 02:56
  • @Samir: If you haven't already, you can click again on the down arrow to reverse the downvote. – Dennis Williamson Oct 17 '17 at 16:44
  • @DennisWilliamson It says that my vote is locked and can not be edited unless someone edits this answer. – Samir Oct 28 '17 at 12:00
  • +1 @WilliamPursell but using `awk "BEGIN {exit !(${result1} > ${result2})}"` instead of piping echo into awk. – CTodea Jan 01 '18 at 21:48
  • @CTodea: Values should be passed into AWK using a pipe or the `-v` argument rather than being inserted using shell string interpolation. – Dennis Williamson Sep 13 '19 at 21:54
  • @DennisWilliamson Your solution with awk is simple and effective. The best answer. Thanks – acgbox Sep 13 '19 at 22:37
9
if awk 'BEGIN{exit ARGV[1]>ARGV[2]}' "$z" "$y"
then
  echo z not greater than y
else
  echo z greater than y
fi
Zombo
  • 1
  • 55
  • 342
  • 375
  • 1
    To those confused as I was with this, in AWK language, value of boolean statement is 1 if true and 0 if not, oppositely to UNIX shell. So in shell scripts that use above AWK program, one has to revert the result, for example use {exit !(ARGV[1]>ARGV[2])} to get shell exit code corresponding to comparison ARGV[1]>ARGV[2]. – Ján Lalinský Dec 10 '18 at 19:10
6
if [[ `echo "$result1 $result2" | awk '{print ($1 > $2)}'` == 1 ]]; then
  echo "$result1 is greater than $result2"
fi
Timor Kodal
  • 144
  • 2
  • 6
4

Following up on Dennis's reply:

Although his reply is correct for decimal points, bash throws (standard_in) 1: syntax error with floating point arithmetic.

result1=12
result2=1.27554e-05


if (( $(echo "$result1 > $result2" | bc -l) )); then
    echo "r1 > r2"
else
    echo "r1 < r2"
fi

This returns incorrect output with a warning although with an exit code of 0.

(standard_in) 1: syntax error
r1 < r2

While there is no clear solution to this (discussion thread 1 and thread 2), I used following partial fix by rounding off floating point results using awk followed by use of bc command as in Dennis's reply and this thread

Round off to a desired decimal place: Following will get recursive directory space in TB with rounding off at the second decimal place.

result2=$(du -s "/home/foo/videos" | tail -n1 | awk '{$1=$1/(1024^3); printf "%.2f", $1;}')

You can then use bash arithmetic as above or using [[ ]] enclosure as in following thread.

if (( $(echo "$result1 > $result2" | bc -l) )); then
    echo "r1 > r2"
else
    echo "r1 < r2"
fi

or using -eq operator where bc output of 1 is true and 0 is false

if [[ $(bc <<< "$result1 < $result2") -eq 1 ]]; then
    echo "r1 < r2"
else
    echo "r1 > r2"
fi
Community
  • 1
  • 1
Samir
  • 686
  • 9
  • 18
0

For shell script I couldn't use double brackets (()). So, what helped me was to split it in two rows and do the comparison in the classic way.

low_limit=4.2
value=3.9
        
result=$(echo "${value}<${low_limit}" | bc)
    
if [ $result = 1 ]; then
  echo too low; 
else 
  echo not too low; 
fi
Marek Manduch
  • 2,062
  • 1
  • 17
  • 22
-1

You can also echo an if...else statement to bc.

- echo $result1 '>' $result2
+ echo "if (${result1} > ${result2}) 1 else 0"

(
#export IFS=2  # example why quoting is important
result1="2.3" 
result2="1.7" 
if [ "$(echo $result1 '>' $result2 | bc -l)" -eq 1 ]; then echo yes; else echo no;fi
if [ "$(echo "if (${result1} > ${result2}) 1 else 0" | bc -l)" -eq 1 ];then echo yes; else echo no; fi
if echo $result1 $result2 | awk '{exit !( $1 > $2)}'; then echo yes; else echo no; fi
)
Zombo
  • 1
  • 55
  • 342
  • 375
progz
  • 279
  • 2
  • 2
-2

Can't bash force type conversion? For example:

($result1 + 0) < ($result2 + 0)
Regent
  • 5,116
  • 3
  • 20
  • 35
  • `()` construct runs the enclosed text as a command, it doesn't do any arithmetic. Looks like you are confusing it with the `((...))` operator. – codeforester Dec 21 '18 at 03:21
-3

Why use bc ?

for i in $(seq -3 0.5 4) ; do echo $i ; if [[ (( "$i" < 2 )) ]] ; then echo "... is < 2";fi; done

The only problem : the comparison "<" doesn't work with negative numbers : they are taken as their absolute value.