Integer expression expected (Bash) - linux

I'm trying to create a simple script that checks if the CPU Usage is at a certain level. I have a Raspberry Pi for this project.
The only issue I have currently is that I have the variable CPU which is the CPU usage, when the script runs, I get the error
line 25: [: 4.8: integer expression expected
I'm not sure why bash returns this if BASH really doesn't care about int, String, etc. like JAVA does. Found that out here.
Also, I have the if statement: if $CPU -gt 1 only for testing (it's supposed to return true right now)
red='\e[0;31m'
yellow='\e[1;33m'
NC='\e[0m' # No Color
echo -e "${NC}Starting Server!${NC}"
echo -e "${red}Errors in red!${NC}"
echo -e "${yellow}Info in yellow!${NC}"
sleep 1
echo -e "\n\n"
##CPU USAGE STUFF
echo -e "${yellow}Current CPU Usage:"
CPU=$(top -bn1 | grep "Cpu(s)" | \
sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | \
awk '{print 100 - $1""}')
echo $CPU %
echo -e "${NC}**********${NC}"
cd /root/mc_server/
sleep 1
##CPU USAGE LOOP CHECKER
while :
do
sleep 5
echo $CPU
if [ $CPU -gt 1 ]
then
echo -e "${red}ALERT! CPU Usage is TOO HIGH!${red}"
fi
done
##
##java -Xmx400M -jar bukkit.jar -o true

The issue is that 4.8 is an non-integer value.
You can use bc to deal with non-integer values like so:
if [[ $(echo "$CPU > 1" | bc -l) -eq 1 ]];
it will return boolean expressions as either 1 for true or 0 for false.

Related

Shell script having problems

#!/bin/bash
while [ true ]
do
echo "$(top -b -o +%MEM -n 1 | head -n +8 | tail -n 1 | awk '{ print $12";"$10 }');$(date +'%H:%M:%S')" >> RAM.csv
if [ -z "$1" ]; then
sleep 1m
else
sleep "$1"
fi
done
ERROR OUTPUT : date: extra operand '%H:%M:%S'
what is wrong with this?
Below you find a mildly improved version of your script:
#!/usr/bin/env bash
# All output is appended to RAM.csv
exec >> RAM.csv
# Do an infinite loop
while :; do
# Get command with most used memory, print it in CSV format, append time
date "+$(ps -eo ucmd -o '%mem' --sort=-%mem h | awk -v OFS=";" '($1=$1);{exit}');%T"
# sleep $1 seconds, if not defined 1m
sleep "${1:-1m}"
done

script in bash for sending mail if CPU exceeds

I have this script:
#!/bin/bash
# rsync using variables
CPU=$(sar 1 5 | grep "Average" | sed 's/^.* //')
if [ $CPU -lt 100 ]
then
cat mail_content.html | /usr/lib/sendmail -t
else
echo "Normal"
fi
when I am executing it I am getting the following error message:
./monitor_cpu.sh: line 6: [: 99.25: integer expression expected
Normal
I want it to be able to identify that the number x.x (99.25) will be recognized which is not happening right now.
Alternatively, if CPU is not an integer, you can use bc to compare it.
echo "$CPU < 100" | bc
Then if the result is 1, the value of CPU will be less than 100. So you can do:
[ $(echo "$CPU < 100" | bc) -eq 1 ] && echo yes
Or, you can convert it to integer with printf:
printf '%.0f' "$CPU"
The problem is that -eq does not compare floats, as explained here. Thus, you need to use bc.
#!/bin/bash
# rsync using variables
CPU=$(LANG=C sar 1 5 | grep "Average" | sed 's/^.* //')
if (( $(echo "$CPU < 100" |bc -l) ))
then
cat mail_content.html | /usr/lib/sendmail -t
else
echo "Normal"
fi

Comparing floating-point numbers in bash [duplicate]

This question already has answers here:
Floating point comparison with variable in bash [duplicate]
(2 answers)
Closed 6 years ago.
In my custom bash script for server monitoring , which actually made to force my CentOS server take some actions and alert me if resources are overloaded more time than expected, I get the following error
line 17: [: 5.74: integer expression expected *
Now by definition all iostat results are float numbers and I already have used awk in my iostat command (WAIT) so how I can make my bash script to expect one instead of integer ?
** Value 5.74 represents current iostat result
#!/bin/bash
if [[ "`pidof -x $(basename $0) -o %PPID`" ]]; then
# echo "Script is already running with PID `pidof -x $(basename $0) -o %PPID`"
exit
fi
UPTIME=`cat /proc/uptime | awk '{print $1}' | cut -d'.' -f1`
WAIT=`iostat -c | head -4 |tail -1 | awk '{print $4}' |cut -d',' -f1`
LOAD=`cat /proc/loadavg |awk '{print $2}' | cut -d'.' -f1`
if [ "$UPTIME" -gt 600 ]
then
if [ "$WAIT" -gt 50 ]
then
if [ "$LOAD" -gt 4 ]
then
#action to take (reboot, restart service, save state sleep retry)
MAIL_TXT="System Status: iowait:"$WAIT" loadavg5:"$LOAD" uptime:"$UPTIME"!"
echo $MAIL_TXT | mail -s "Server Alert Status" "mymail#foe.foe"
/etc/init.d/httpd stop
# /etc/init.d/mysql stop
sleep 10
# /etc/init.d/mysql start
/etc/init.d/httpd start
fi
fi
fi
CentOS release 6.8 (Final) 2.6.32-642.13.1.el6.x86_64
Normally, you'd need to use something other than native shell math, as described in BashFAQ #22. However, since you're comparing to integers, this is easy: You can just truncate at the decimal point.
[ "${UPTIME%%.*}" -gt 600 ] # truncates your UPTIME at the decimal point
[ "${WAIT%%.*}" -gt 50 ] # likewise

Grep multiple bash parameters

I'm writing a bash script which shall search in multiple files.
The problem I'm encountering is that I can't egrep an undetermined number of variables passed as parameters to the bash script
I want it to do the following:
Given a random number of parameters. i.e:
./searchline.sh A B C
Do a grep on the first one, and egrep the result with the rest:
grep "A" * | egrep B | egrep C
What I've tried to do is to build a string with the egreps:
for j in "${#:2}";
do
ADDITIONALSEARCH="$ADDITIONALSEARCH | egrep $j";
done
grep "$1" * "$ADDITIONALSEARCH"
But somehow that won't work, it seems like bash is not treating the "egrep" string as an egrep.
Do you guys have any advice?
By the way, as a side note, I'm not able to create any auxiliary file so grep -f is out of the line I guess. Also note, that the number of parameters passed to the bash script is variable, so I can't do egrep "$2" | egrep "$3".
Thanks in advance.
Fernando
You can use recursion here to get required number of pipes:
#!/bin/bash
rec_egrep() {
if [ $# -eq 0 ]; then
exec cat
elif [ $# -eq 1 ]; then
exec egrep "$1"
else
local pat=$1
shift
egrep "$pat" | rec_egrep "$#"
fi
}
first_arg="$1"
shift
grep "$first_arg" * | rec_egrep "$#"
A safe eval can be a good solution:
#!/bin/bash
if [[ $# -gt 0 ]]; then
temp=("grep" "-e" "\"\$1\"" "*")
for (( i = 2; i <= $#; ++i )); do
temp=("${temp[#]}" "|" "egrep" "-e" "\"\$$i\"")
done
eval "${temp[#]}"
fi
To run it:
bash script.sh A B C

Creating bash script of a complex linux command

I have few long commands that I will be using on a day to day basis. So I though it would be better to have a bash script where I could pass arguments, thus saving typing. I guess this is the norm in Linux but I am kind of new to it. Could someone show me how to do it. A example is the following command
cut -f <column_number> <filename> | sort | uniq -c |
sort -r -k1 -n | awk '{printf "%-15s %-10d\n", $2,$1}'
so i want this in a script where i can pass the filename and column number (preferably in any order) and get the desired ouput instead of having to type the whole thing everytime.
Create a file say myscript.sh -
#!/bin/bash
if [ $# -ne 2 ]; then
echo Usage: myscript.sh column_number file_path
exit
fi
if ! [ -f $2 ]; then
echo File doesnt exist
exit
fi
if [ `echo $1 | grep -E ^[0-9]+$ | wc -l` -ne 1 ]; then
echo First argument must be a number
exit
fi
cut -f 10 $1 $2 | sort | uniq -c |
sort -r -k1 -n | awk '{printf "%-15s %-10d\n", $2,$1}'
Make sure this file is executable using command chmod +x mytask.sh
You can invoke it like sh myscript.sh 30 myfile.sh or ./myscript.sh 30 myfile.sh
The first line of above script specifies the shell you would like your script to be executed in. $1 and $2 refer to the first and second command line arguments.
About argument validity checks:
First check ensures that there are exactly two arguments passed to the script.
Second check ensures the file pointed by the argument two is existing
Third check ensures that the number passed as first argument is really a number. It uses regular expression for that purpose. May be someone provide a better replacement for this check but this is what came to my mind instantly.
To accept the filename and column number in any order, you'll need to use option switches. Bash's getopts allows you to specify and process options so you can call your script using scriptname -f filename -c 12 or scriptname -c 12 -f filename for example.
#!/bin/bash
options=":f:c:"
while getopts $options option
do
case $option in
f)
filename=$OPTARG
;;
c)
col_num=$OPTARG
;;
\?)
usage_function # not shown
exit 1
;;
*)
echo "Invalid option"
usage_function
exit 1
;;
esac
done
shift $((OPTIND - 1))
if [[ -z $filename || -z $col_num ]]
then
echo "Missing option"
usage_function
exit 1
fi
if [[ $col_num == *[^0-9]* ]]
then
echo "Invalid integer"
usage_function
exit 1
fi
# other checks
cut -f 10 $col_num "$filename" | ...

Resources