1

I wrote a little bash-script to check my Memory-usage and warn me if it's to high.

now my problem is, that I would like to keep the floating-value instead of just cutting it away. but I'm not able to do it..

I would prefer awk over anything else as it's pre-installed on many systems and I already use it in the script.

#!/bin/bash
#define what values are too high (%)
inacceptableRAM="90"
#check RAM %
RAM=`free | grep Mem | awk '{print $3/$2 * 100.0}'`
#send Alarm for RAM
if [ $RAM -ge $inacceptableRAM ] ; then
   echo Alarm RAM usage is @$RAM"
fi

so how can I replace my if -ge using awk? I think it should look something like: awk ' $RAM >= $inacceptableRAM ' but what do I need to do to make it work inside the bash script?

friendly joe
  • 139
  • 1
  • 2
  • 7
  • `cat /proc/meminfo | head -2` – Alex Angel Nov 20 '21 at 02:38
  • Please do what the `bash` tag tells you do do before posting a question - copy/paste your script into http://shellcheck.net, fix the issues the tool tells you about, then [edit] your question to show that corrected script instead of the one you currently have if you still have a problem so we don't have to consider the impact of those bugs while trying to help you. – Ed Morton Nov 20 '21 at 12:10
  • I'm reopening this because the question it was closed as a dup of, while technically a partial answer to the question the OP asked, will help them implement the wrong solution instead of showing them how to create the right solution. – Ed Morton Nov 20 '21 at 12:27

4 Answers4

2

Since you're comparing with an integer, you can just trim off the decimal part when comparing:

if [ "${RAM%.*}" -ge "$inacceptableRAM" ] ; then

If you want to do it entirely in awk, the only tricky thing is that you have to use -v var=value to convert the inacceptableRAM shell variable into an awk variable:

free | awk -v limit="$inacceptableRAM" '/Mem/ {ram=$3/$2*100; if (ram>=limit) print ram}'

Note that I'm using /Mem/ in the awk script to effectively replace the grep command. Piping from grep to awk is almost never necessary, since you can just do it all in awk.

Other recommendations: use $( ) instead of backticks for command substitutions (see BashFAQ #82), and use lower- or mixed-case variable names (e.g. ram instead of RAM) to avoid accidentally using one of the many all-caps names that have special meanings (see this answer).

Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151
  • can I also do something inside of bash or another tool with the result? like instead of using print, use echo in bash? – friendly joe Nov 21 '21 at 11:29
  • @friendlyjoe With the `awk` version? You'd have to capture its output with something like `result=$(free | awk ...)`, and then both print it and do something else with that result. If you want the number in bash, it's probably easier to have `awk` print just the number, and then do everything else in bash. – Gordon Davisson Nov 21 '21 at 18:16
  • +1 for just truncating the `RAM` since the other operand is an integer anyways. IMO that's simpler and clearer than outsourcing to another tool. – dimo414 Nov 22 '21 at 22:02
1

An alternative to awk is bc , something like:

#!/usr/bin/env bash

#define what values are too high (%)
inacceptableRAM="90"

#check RAM %
ram=$(free | awk '/Mem/{print $3/$2 * 100.0}')

#send Alarm for RAM
if (( $(bc <<< "$ram > $inacceptableRAM") )) ; then
   echo "Alarm RAM usage is @$ram"
fi
Jetchisel
  • 7,493
  • 2
  • 19
  • 18
1

You're trying to do too much in shell. A shell is a tool to manipulate files and process and sequence calls to other tools. The tool that the guys who created shell also created for shell to call to manipulate text is awk:

#!/usr/bin/env bash

free |
awk '
    BEGIN {
        #define what values are too high (%)
        unacceptableRam = 90
    }
    /Mem/ {
        #check RAM %
        ram = ( $2 ? $3 / $2 * 100 : 0 )

        #send Alarm for RAM
        if ( ram >= unacceptableRam ) {
            print "Alarm RAM usage is @" ram
        }
    }
'
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
1

It's worth considering if it's time for you to "upgrade" to a proper programming language with support for features like floating point arithmetic. Bash and shell scripting is great, but it runs up against limitations very quickly. Even Google's Shell Style Guide suggests changing languages when things get complicated. It's likely you could get a Python script doing exactly what your Bash script is doing with just a few more lines of code.

That said, I am very guilty of leaning on Bash even when I probably shouldn't :)

I recently wanted to compute a floating point average in a Bash script and decided to delegate to a Python subprocess just for that task, rather than swap over fully to Python. I opted for Python because it's almost as ubiquitous as Bash, easy to read, and extensible. I considered bc but was worried it might not be installed on all systems. I didn't consider awk because, as great a tool as it is, its syntax is often opaque and arcane. Python is probably slower than bc or awk but I really don't care about that incremental performance difference. If I did care that would be strong evidence I should rewrite the whole script in a performant language.

For your use case Jetchisel's suggestion to use bc <<< "$ram > $inacceptableRAM" is probably sufficient, as the Python equivalent is more verbose, but for something more complex Python may be a good choice.

dimo414
  • 47,227
  • 18
  • 148
  • 244