3

I'm trying to get a script to do something when one number is bigger than another, in this case, when the system load average exceeds a defined value.

All of it is working aside from the evaluation criteria.

Whilst I build another server for some sites, I'm trying to keep apache in line, the reasons are not important but this script has been tested and tested on a system where the load average is below 15 and the script prints out:

"check is 4.68 and max is 15.00" DESPITE the if condition telling it not to print anything at all if the value of check is not greater than max load, which it isn't.

I'm no bash guru, I have a beard but no sandals and I've tried a variety of differing styles of brackets and quoting but I can't figure out why this script prints anything at all when $check is less than $max_load.

This is on Debian 6, GNU bash, version 4.1.5(1)-release-(x86_64-pc-linux-gnu)

#!/bin/bash
check=`cat /proc/loadavg | sed 's/./ /' | awk '{print $2}'`
max_load='15.00';
high_load_log='/var/log/apache2/apache_high_load_restart.log';
apache_init='/etc/init.d/apache2';

if [[ $check > $max_load ]]; then
   echo " check is $check and max is $max_load";
   #$apache_init stop
   sleep 5;
   #$apache_init restart
   echo "$(date) : Apache Restart due to load of | $check |" >> $high_load_log;
fi

On a system with a load of about 4, this script outputs:

"check is 4.68 and max is 15.00"

Does anyone know why?

Any help, and suggestion for good starter sandals would be much appreciated!

  • Honestly, I would be tempted to add a set -x at the top of your script, so you get verbose echoing of what is going on. That often is very helpful for debugging. – Zoredache Mar 4 '15 at 23:16
  • Your cat | sed | awk abomination seems to only pick out the decimal part of the load average and using it as an integer. The diagnostics you show are not consistent with this. Are you sure this is the code you are actually running? – tripleee Mar 6 '15 at 13:55
3

This isn't going to work. The > operator inside [[ compares the sort order, not the value. So....

$ echo -e '4.68\n15.00'|sort
15.00
4.68

... because 4 sorts after 1, which means [[ 4.68 > 15.00 ]] is true. And you can't use -gt, because that requires integers.

If you only care about integer thresholds, that's the easy fix — truncate at the ., use -gt, and there you go. Otherwise, use bc — see https://unix.stackexchange.com/questions/24721/how-to-compare-to-floating-point-number-in-a-shell-script

| improve this answer | |
  • for those not seeing it the sort compares the character '4' to the character '1'. – Skaperen Mar 5 '15 at 11:21
0

According to the docs, the <, and > are a lexical sort. I am not certain, but I am pretty sure this is the same as what you would get if you used something like sort. With sort, 15.00 is before 4.68 because it basically sorts character by character. So 1 is before 4 in most locales.

Since your example value 4.68 would be after 15.0 sorted lexically, the > returns true.

You almost certainly wanted your strings to be treated as numbers so you would use -gt, or -lt, but these are limited to integers.

Ref: http://www.gnu.org/software/bash/manual/html_node/Bash-Conditional-Expressions.html

string1 < string2 True if string1 sorts before string2 lexicographically.

string1 > string2 True if string1 sorts after string2 lexicographically.

| improve this answer | |
0

Thanks all, the final solution which works well on my Debian system was this, essentially converting it to an int first then using -gt.

#!/bin/bash
check=`cat /proc/loadavg | sed 's/ / /' | awk '{print $1}'`
checkint=${check/\.*}
max_load='20';
high_load_log='/var/log/apache2/apache_high_load_restart.log';
apache_init='/etc/init.d/apache2';

if [ $checkint -gt $max_load ]; then
        echo " check is $checkint and max is $max_load";
        $apache_init stop
        sleep 5;
        $apache_init restart
        echo "$(date) : Apache Restart due to excessive load | $check |" >> $high_load_log;
else
        echo "check is $check resolving down to $checkint and max is $max_load - No Action Taken";
fi
| improve this answer | |
0

The fundamental problem is that Bash only supports integer arithmetic. You can work around this by using a tool which supports floating point, which conveniently Awk does.

(I would also factor out the useless cat and note that sed | awk is similarly useless.)

awk -v max="$max_load" '$1 > max {
    print "check is " $1 " and max is " max }' /proc/loadavg

If you want to use this in a shell conditional, make Awk return a zero exit code on success, non-zero on failure:

if ! check=$(awk -v max="$max_load" '($1 > max) {
       print $1; exit 1 }' /proc/loadavg); then
   echo " check is $check and max is $max_load";
   $apache_init stop
   sleep 5;
   $apache_init restart
   date +"%c : Apache Restart due to load of | $check |" >> $high_load_log;
fi
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.