1

Have tried several different syntax methods to make bash test between number ranges for floating point numbers and cannot get this to work. Whole numbers work, so do statements without the && operator. I must be missing something obvious.

Essentially 70 and below is "ok", between 70.1 and 79.9 is "warn", 80 and above is "critical"

Thanks in advance for any help or advice.

#! /bin/bash

number=70.1
echo $number

if (( $(echo "$number < 70" | bc -l) )); then echo "OK";fi
if (( $(echo "$number >= 70" && "$number < 80" | bc -l) )); then echo "WARN";fi
if (( $(echo "$number >= 80" | bc -l) )); then echo "CRITICAL";fi
Rob Qlder
  • 35
  • 1
  • 6

3 Answers3

2

echo "$number >= 70" && "$number < 80" are two commands in bash. The first is echo and the second command is "70.1 < 80" (pretty sure there is no such command on your system).

You probably wanted to write echo "$number >= 70 && $number < 80" which is just one command.

By the way: In bash you can use bc <<< ... instead of echo ... | bc.

if (( $(bc <<< "$number < 70") )); then echo "OK"; fi
if (( $(bc <<< "$number >= 70 && $number < 80") )); then echo "WARN"; fi
if (( $(bc <<< "$number >= 80") )); then echo "CRITICAL"; fi

or with restructured control flow

if (( $(bc <<< "$number < 70") )); then
  echo "OK";
elif (( $(bc <<< "$number < 80") )); then
  echo "WARN"
else
  echo "CRITICAL"
fi
Socowi
  • 25,550
  • 3
  • 32
  • 54
  • solved my problem and a very clear explanation too. Thanks for helping out, I changed to the control flow option you provided as the final solution. – Rob Qlder May 17 '20 at 04:13
1

This should be a single awk command.

awk -v n="$number" 'BEGIN {
  if      (n < 70) { print "OK"; }
  else if (n < 80) { print "WARN"; }
  else             { print "CRITICAL"; }
}'
chepner
  • 497,756
  • 71
  • 530
  • 681
  • Instead of `awk -v n="$number" 'BEGIN { ... n ... }'` you could also use the slightly hacky but shorter and more portable version `awk '{ ... $0 ... }' <<< "$number"`. – Socowi May 16 '20 at 13:17
  • That's quite a bit *less* portable: `<<<` is a shell-specific extension to the POSIX standard. This answer uses no extensions to the POSIX standard for `awk`. – chepner May 16 '20 at 13:33
  • Oh wow, I always thought `-v` was a GNU extension. Seems I was wrong. Thank you for pointing this out. ¶ I don't see `<<<` as a portability problem here as OP explicitly wrote `#! /bin/bash`. – Socowi May 16 '20 at 13:47
  • thanks, I confirmed this solved my issue. I went with the other solution as I was already using bc. Good to know awk can be used in this way. – Rob Qlder May 17 '20 at 04:15
  • @Socowi There's also a marginal performance issue. A here-string is just syntactic sugar for a one-line here document, and here documents are implemented as temporary files. The `awk` command isn't doing any I/O at all. – chepner May 17 '20 at 12:59
0

Since the comparison ranges are positive integers¹, these can be compared natively by the shell if $number is first converted to an integer:

#!/usr/bin/env bash

# Defines the locale of our floating-point numbers
LC_NUMERIC=POSIX
number=70.1
echo $number

declare -i intnum

# Get decimal_point from locale (can be comma or anything locale specific)
dp="$(locale decimal_point)"

# Cut fractional part of floating-point number to a whole integer intnum
intnum="${number%$dp*}"

# Shell natively compares integer
if [ $intnum -lt 70 ]; then
  echo 'OK';
elif [ $intnum -lt 80 ]; then
  echo 'WARN'
else
  echo 'CRITICAL'
fi

  1. The integer comparison only works for positive numbers.
    (-1.1 < -1 but -1 = -1)
Léa Gris
  • 17,497
  • 4
  • 32
  • 41
  • 1
    But this would print `WARN` for `number=69.9` when it should print `OK`. To fix the problem you could truncate instead of round. Replace `printf` by `intnum=${number/.*/}`. To allow negative numbers you could use bash `(( … < … ))` instead of posix `[ … -lt … ]`. – Socowi May 16 '20 at 13:21
  • @Socowi I just expanded and applied your suggestion with: `intnum="${number%.*}"` – Léa Gris May 16 '20 at 13:27