BC calculation for floating (invalid arithmetic operator )

Hi,

I wish to compare the CPU LOAD 1 min with 5mins and 15mins.
If 1 min's CPU LOAd spike 3% compare to 5 mins or 15 mins CPU Load, it is warning.
If 1 min's CPU LOAd spike 5% compare to 5 mins or 15 mins CPU Load, it is critical.

However I received following code error, I google it and tried few method it still the same.

 ./CPULOAD: line 15: [[: 1.98: syntax error: invalid arithmetic operator (error token is ".98")
./CPULOAD: line 19: [[: 1.94: syntax error: invalid arithmetic operator (error token is ".94")
#!/bin/ksh
str1=`uptime | awk '{print $10}'| sed 's/,//'`
str2=`uptime | awk '{print $11}'| sed 's/,//'`
str3=`uptime | awk '{print $12}'| sed 's/,//'`
 echo $str1
echo $str2
echo $str3
#warn_level='$str1*1.03' | bc
warn_level=`echo "$str1*1.03"|bc`
crit_level=`echo "$str1*1.05"|bc`
#crit_level='$str1*1.05' | bc
 if [[ "$crit_level" -gt "$str2" || "$crit_level" -gt "$str3" ]]; then
        status=2
        statustxt="CPU load is critical - $str1"
 elif [[ "$warn_level" -gt "$str2" || "$warn_level" -gt "$str3" ]]; then
        status=1
        statustxt="CPU load is Warning - $str1"
else
        status=0
        statustxt="CPU load is normal, no spike - $str1"
fi
 echo "$status CPULOAD_spike  PERFDATA=$str1 $statustxt"

May I know which part could be wrong?

What version of ksh are you using?
Is this a Linux kernel base operating system?

if [[ "$crit_level" -gt "$str2" || "$crit_level" -gt "$str3" ]]; then
elif [[ "$warn_level" -gt "$str2" || "$warn_level" -gt "$str3" ]];

Something to think about:
What does the -gt operator works with: strings or numbers? What are you comparing: strings or numbers?
Does your shell support floating point arithmetic?

1 Like

As Aia noted, the version of the Korn shell you're using makes a big difference here. With a 1993 or later version of ksh , your code might work. I'm not sure in this case that the OS matters much, but when asking questions like this, it is always a good policy to tell us what environment you're using.

The fact that the error messages that ksh printed did not come from the script you showed us makes us wonder what the other two lines before the if statement looked like and what the line that you removed before the elif looked like???

And, the fact that you're using sed to remove commas from the uptime output makes us wonder what locale you're using. The text in your output is English, but if you're removing commas, is the locale European or are the actions on the commas just there to confuse us???

Please show us:

  1. the actual code that produced those error messages,
  2. the output you get from running the command: /bin/ksh --version ,
  3. the output you get from running the command: locale ,
  4. and the output you get from running the command: uptime

(note that when displaying the output you get from running commands, the input that is given to commands, and code segments you should use CODE tags; not just when showing us code segments).

I would guess that you're using a 1988 version of ksh , but until I figure out what you're trying to do with the commas, I'm not sure how to address your problem.

1 Like

Hi Aia,

It is SUSE SLEH 11, I am comparing the 'number'.

Hi Don,

  1. The below code is the actual full code showing this error (line 15 and 19)
#!/bin/ksh
str1=`uptime | awk '{print $10}'| sed 's/,//'`
str2=`uptime | awk '{print $11}'| sed 's/,//'`
str3=`uptime | awk '{print $12}'| sed 's/,//'`
 echo $str1
echo $str2
echo $str3
#warn_level='$str1*1.03' | bc
warn_level=`echo "$str1*1.03"|bc`
crit_level=`echo "$str1*1.05"|bc`
#crit_level='$str1*1.05' | bc
 if [[ "$crit_level" -gt "$str2" || "$crit_level" -gt "$str3" ]]; then
        status=2
        statustxt="CPU load is critical - $str1"
 elif [[ "$warn_level" -gt "$str2" || "$warn_level" -gt "$str3" ]]; then
        status=1
        statustxt="CPU load is Warning - $str1"
else
        status=0
        statustxt="CPU load is normal, no spike - $str1"
fi
 echo "$status CPULOAD_spike  PERFDATA=$str1 $statustxt"

 
 
 /bin/ksh --version: version         sh (AT&T Research) 93u+ 2012-08-01
  1. locale:
LANG=POSIX
LC_CTYPE=en_US.UTF-8
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=
  1. uptime
15:02pm  up 77 days  0:26,  1 user,  load average: 1.82, 1.89, 1.88

Below is the exactly output from the code

1.82
1.89
1.88
./CPULOAD: line 15: [[: 1.91: syntax error: invalid arithmetic operator (error token is ".91")
./CPULOAD: line 15: [[: 1.91: syntax error: invalid arithmetic operator (error token is ".91")
./CPULOAD: line 19: [[: 1.87: syntax error: invalid arithmetic operator (error token is ".87")
./CPULOAD: line 19: [[: 1.87: syntax error: invalid arithmetic operator (error token is ".87")
0 CPULOAD_spike  PERFDATA=1.82 CPU load is normal, no spike - 1.82

I removed the comma due to when

awk '{print $10}'

the output consists of comma

uptime | awk '{print $10}' 

below is the output
1.84,

uptime | awk '{print $10}'| sed 's/,//' 

below is the output
1.84

The OS would be significant, since the output of uptime is not the same.
Also, if it is a Linux kernel /proc/loadavg could be used instead of uptime .

cat /proc/loadavg
0.00 0.01 0.05 1/133 10331

Instead of dealing with another external program and the output would not have comas:

uptime
 01:15:03 up 39 days,  8:39,  1 user,  load average: 0.00, 0.01, 0.05
1 Like

Hi Aia,

The reason I use uptime instead of /proc/loadavg because I wish to use same coding for Solaris, I see the Solaris uptime same as mine SUSE.

So any idea why the calculation showing error?:slight_smile:

Does your Solaris system use the same version of ksh?
ksh93 supports floating point. You do not need the external bc program
Something like

warn_level=$(( $str1 * 1.03 ))

Also, remove the quotes

if [[ "$crit_level" -gt "$str2" || "$crit_level" -gt "$str3" ]];

Those are strings and not numbers.

1 Like

Hi Aia,

Thanks for the quick help, this resolved the problem

I'm still confused by this thread. With the script you showed us, it should work just fine (with or without double quotes around the expansions of the variables your [[ expression ]] statements) if t is run by a 1993 or later version of the Korn shell (including version 93u+ , which is what you say you are using).

The diagnostic messages you showed us are something that I would expect to see if you executed a script similar to what you showed us using bash as your shell instead of ksh . And, I say a script similar to (but not the script you showed us) because the diagnostics point to problems in the [[...]] statements on lines 15 and 19 in your script; but the script you showed us has those statements on lines 12 and 15 (NOT 15 and 19).

But, running successfully and producing the results you want are two different issues. From your problem statement, I thought you wanted to produce a warning or critical message if field 10 in the uptime output is 3% or 5%, respectively, higher than the lower or the field 11 or field 12 values. (I.e., the current 1 minute value is greater than the 5 and 15 minute values.) But, the code you showed us (if run by a 1993 or later ksh ), if the field 11 or 12 value is less than 103% or 105% of the field 10 value. So, for example, if fields 10, 11, and 12 all contained the value 1.00 (showing absolutely no increase in load in the last 15 minutes; your script would report a critical condition because 105% of the field 10 value (1.05) is greater than the field 11 and 12 values (1.00). Is this really what you want?

The following script should do what you want on your Linux system. If you also have a 1993 or later version of ksh on your Solaris system (which might only be true if you're running a Solaris 11.0 or later system), the following would do what I think you described in your requirements just using shell built-ins. I must say, however, that I don't understand the need to include the current load value (as contained in $str1 ) in each of your three possible output messages) twice.

#!/bin/ksh
set -- $(uptime)
str1=${10%,}
str2=${11%,}
str3=${12}
echo str1:$str1
echo str2:$str2
echo str3:$str3
warn_level=$((1.03 * (str2 < str3 ? str2 : str3)))
crit_level=$((1.05 * (str2 < str3 ? str2 : str3)))
echo warn:$warn_level
echo crit:$crit_level
if [[ str1 -gt crit_level ]]
then	status=2
	statustxt="CPU load is critical - $str1"
elif [[ str1 -gt warn_level ]]
then	status=1
	statustxt="CPU load is Warning - $str1"
else	status=0
	statustxt="CPU load is normal, no spike - $str1"
fi
echo "$status CPULOAD_spike  PERFDATA=$str1 $statustxt"

If you need something that will work on a system running a Solaris 10 or earlier release without a ksh93 , and you find that the above script does what you want on your Linux system, we'll need to look for an alternative solution.

1 Like

Hi Don,

Apology for the confuse, the line different due to I remove the 3 lines 'echo', I did not notice it.

I tried your code works fine in Solaris and SUSE! Your code is so neat :slight_smile:
May I know what is the meaning for below coding?

warn_level=$((1.03 * (str2 < str3 ? str2 : str3)))

I found my logic mistake and changed as below yesterday (divide result from 1min CPU LOAD with 5mins and 15mins, if it is less than 1.03 (3%) then it is normal, if more than 1.03 (3) then is warning, if more than 1.05 (5%) it is critical.).

1 min load 3% < 5mins and 15mins is normal
1 min load 3% > 5mins or 15mins is warning
1 min load 5% > 5mins or 15mins is critical

#!/bin/ksh
str1=`uptime | awk '{print $10}'| sed 's/,//'`
str2=`uptime | awk '{print $11}'| sed 's/,//'`
str3=`uptime | awk '{print $12}'| sed 's/,//'`
 warn_level=$(( 1.03 ))
crit_level=$(( 1.05 ))
 if [[ $str1/$str2 -gt $crit_level || $str1/$str3 -gt $crit_level ]]; then
        status=2
        statustxt="CPU load is critical - $str1"
 elif [[ $str1/$str2 -gt $warn_level || $str1/$str3 -gt $warn_level ]]; then
        status=1
        statustxt="CPU load is Warning - $str1"
else
        status=0
        statustxt="CPU load is normal, no spike - $str1"
fi
 echo "$status CPULOAD_spike  PERFDATA=$str1 $statustxt"

The assignment command:

warn_level=$((1.03 * (str2 < str3 ? str2 : str3)))

assigns the result of the arithmetic expression:

1.03 * (str2 < str3 ? str2 : str3)

to the variable warn_level . As in the awk programming language and in the C programming language, the expression:

expr1 ? expr2 : expr3

evaluates expr1 and if it yields a non-zero value the result of the above expression is whatever expr2 evaluates to; otherwise, the result of the above expression is whatever expr3 evaluates to. In this case, the entire expression evaluates to the multiplication of 1.03 times the minimum of $str2 and $str3 .

Note also that using the shell's set special built-in, three variable assignments and 2 variable expansions is MUCH more efficient that using three command assignments each of which invokes a command substitution which in turn also invokes the external utility awk and the external utility sed .