Shell - Compare Less Than Double - shell

#!/bin/bash
# Obtain the server load
loadavg=`uptime |cut -d , -f 4|cut -d : -f 2`
thisloadavg=`echo $loadavg|awk -F \. '{print $1}'`
if [ "$thisloadavg" -eq "0.01" ]; then
ulimit -n 65536
service nginx restart
service php-fpm restart
fi
The error is:
./loadcheck.sh: line 7: [: 0.01: integer expression expected
I want to do a loadcheck shell script that can compare double instead of integer, as i want assured the load return is less than 0.01 which is 0.00 ,
If I use 0, even if the load is 0.05 , it will still execute the code.

in zsh you can simply use:
if [[ "$thisloadavg" < "0.01" ]]; then
the double [[ construct allows extra tests and in zsh it allows floating point tests.

Bash can't deal with floating point values, so you'll need to use additional commands such as awk, expr or bc to do it for you. For example, using bc:
loadavg=$(uptime | cut -d, -f4 |cut -d: -f2)
low_load=$(echo "$loadavg < 0.01" | bc -l)
if [ $low_load -eq 1 ]; then
# do stuff
fi

Related

Assing a variable and use it inside of if statement shell scripting

I am new to shell scripting and trying to make a few small scripts. I got stuck when i tried to write if condition. In the code below i am trying to get the $5 value from df and trying to use it in if condition. However the code doesn't work.
#!/bin/sh
temp = $(df -h | awk '$NF=="/"{$5}')
if [ $temp > 60 ] ; then
df -h | awk '$NF=="/" {printf("%s\n"),$5}'
(date -d "today" +"Date:%Y.%m.%d"" Hour: %H:%M")
fi
#end
So i've figured out something and changed my code into this:
temp=$(df -h | awk '$NF=="/"{$5}')
if [ "$((temp))" -gt 0 ] ; then
df -h | awk '$NF=="/" {printf("%s\n"),$5}'
(date -d "today" +"Date:%Y.%m.%d"" Hour: %H:%M")
fi
#end
And now , i'm trying to get the integer value of the $5 variable. It returns a percentage and i want to compare this percentage with %60. How can i do that ?
Let's see what shellcheck.net has to tell us:
Line 1:
#!/bin/sh
^-- SC1114: Remove leading spaces before the shebang.
Line 3:
temp = $(df -h | awk '$NF=="/"{$5}')
^-- SC1068: Don't put spaces around the = in assignments.
Line 4:
if [ $temp > 0 ] ; then
^-- SC2086: Double quote to prevent globbing and word splitting.
^-- SC2071: > is for string comparisons. Use -gt instead.
^-- SC2039: In POSIX sh, lexicographical > is undefined.
Um, ok, after a little fixing:
#!/bin/sh
temp=$(df -h | awk '$NF=="/"{$5}')
if [ "$temp" -gt 0 ] ; then
df -h | awk '$NF=="/" {printf("%s\n"),$5}'
(date -d "today" +"Date:%Y.%m.%d"" Hour: %H:%M")
fi
The [ ... ] command is the same as test command. Test does not have < comparison for numbers. It has -gt (greater then). See man test.
This will run now, but definitely not do what you want. You want the fifth column of df output, ie. the use percents. Why do you need -h/human readable output? We dont need that. Which row of df output do you want? I guess you don't want the header, ie. the first row: Filesystem 1K-blocks Used Available Use% Mounted on. Let's filter the columns with the disc name, I choose /dev/sda2. We can filter the row that has the first word equal to /dev/sda2 with grep "^/dev/sda2 ". The we need to get the value on the fifth column with awk '{print $5}'. We need to get rid of the '%' sign too, otherwise shell will not interpret the value as a number, with sed 's/%//' or better with tr -d '%'. Specifying date -d"today" is the same as just date. Enclosing a command in (...) runs it in subshell, we don't need that.
#!/bin/sh
temp=$(df | grep "^/dev/sda2 " | awk '{print $5}' | tr -d '%')
if [ "$temp" -gt 0 ]; then
echo "${temp}%"
date +"Date:%Y.%m.%d Hour: %H:%M"
fi
This is a simple, that if use percentage on disc /dev/sda2 is higher then 0, then it will print the use percentage and print current date and time in a custom format.
Assuming you're using GNU tools, you can narrow the df output to just what you need:
pct=$( df --output=pcent / | grep -o '[[:digit:]]\+' )
if [[ $pct -gt 60 ]]; then ...

Converting string to floating point number without bc in bash shell script

I'm getting load average in a bash shell script like so
load=`echo $(cat /proc/loadavg | awk '{print $1}')`
I know piping to bc
load=`echo $(cat /proc/loadavg | awk '{print $1}') \> 3 | bc -l`
is used in almost all examples of how to cast $load as an int but this box does not have bc installed and I am not allowed to add it.
I tried
int=`perl -E "say $load - 0"`
I tried
int=${load%.*}
I tried
int=`printf -v int %.0f "$load"`
What I want to be able to do is
if [ "$int" -gt 3.5 ]; then
How do I get that to evaluate as intended?
You can use awk to produce a success/failure depending on the condition:
# exit 0 (success) when load average greater than 3.5, so take the branch
if awk '{ exit !($1 > 3.5) }' /proc/loadavg; then
# load average was greater than 3.5
fi
Unfortunately, since "success" is 0 in the shell, you have to invert the logic of the condition to make awk exit with the required status. Obviously, you can do this in a number of ways, such as changing > to <=.
You don't need any external tools (like awk) to read this stuff. Load average from /proc/loadavg is always formatted with two decimal places, so you can do this:
read load _ < /proc/loadavg
if [ ${load/./} -gt 350 ]; then
# do something
fi

Convert Floating Point Number To Integer Via File Read

I'm trying to get this to work when the "line" is in the format ###.###
Example line of data:
Query_time: 188.882
Current script:
#!/bin/bash
while read line; do
if [ $(echo "$line" | cut -d: -f2) -gt 180 ];
then
echo "Over 180"
else
echo "Under 180"
fi
done < test_file
Errors I get:
./calculate: line 4: [: 180.39934: integer expression expected
If you have:
line='Query_time: 188.882'
This expression:
$(echo "$line" | cut -d: -f2) -gt 180
Will give an error invalid arithmetic operator since BASH cannot handle floating point numbers.
You can use this awk command:
awk -F ':[ \t]*' '{print ($2 > 180 ? "above" : "under")}' <<< "$line"
above
You can use this awk:
$ echo Query_time: 188.882 | awk '{ print ($2>180?"Over ":"Under ") 180 }'
Over 180
It takes the second space delimited field ($2) and using conditional operator outputs if it was over or under (less than or equal to) 180.

get memory from ps and compare via bash with my limit

I have code that I use in openwrt. I need to check memory which use application
#!/bin/bash
VAR=$(ps | grep sca | grep start | awk '{print $3}')
VAG=$(cat /proc/pid/status | grep -e ^VmSize | awk '{print $2}')
if [ $VAG>28000 ]
then
echo test
fi
No matter if I use VAR or VEG(for example VAR/VAG equal 15000), I can get work this code. I always get "test"
Your if statement is incorrect. The test command (aka [) must receive separate arguments for the operands and the operator. Also, > is for string comparisons; you need to use -gt instead.
if [ "$VAG" -gt 28000 ]
Since you are using bash, you can use the more readable arithmetic command instead of [:
if (( VAG > 28000 ))

Bash script checking cpu usage of specific process

First off, I'm new to this. I have some experience with windows scripting and apple script but not much with bash. What I'm trying to do is grab the PID and %CPU of a specific process. then compare the %CPU against a set number, and if it's higher, kill the process. I feel like I'm close, but now I'm getting the following error:
[[: 0.0: syntax error: invalid arithmetic operator (error token is ".0")
what am I doing wrong? here's my code so far:
#!/bin/bash
declare -i app_pid
declare -i app_cpu
declare -i cpu_limit
app_name="top"
cpu_limit="50"
app_pid=`ps aux | grep $app_name | grep -v grep | awk {'print $2'}`
app_cpu=`ps aux | grep $app_name | grep -v grep | awk {'print $3'}`
if [[ ! $app_cpu -gt $cpu_limit ]]; then
echo "crap"
else
echo "we're good"
fi
Obviously I'm going to replace the echos in the if/then statement but it's acting as if the statement is true regardless of what the cpu load actually is (I tested this by changing the -gt to -lt and it still echoed "crap"
Thank you for all the help. Oh, and this is on a OS X 10.7 if that is important.
I recommend taking a look at the facilities of ps to avoid multiple horrible things you do.
On my system (ps from procps on linux, GNU awk) I would do this:
ps -C "$app-name" -o pid=,pcpu= |
awk --assign maxcpu="$cpu_limit" '$2>maxcpu {print "crappy pid",$1}'
The problem is that bash can't handle decimals. You can just multiply them by 100 and work with plain integers instead:
#!/bin/bash
declare -i app_pid
declare -i app_cpu
declare -i cpu_limit
app_name="top"
cpu_limit="5000"
app_pid=`ps aux | grep $app_name | grep -v grep | awk {'print $2'}`
app_cpu=`ps aux | grep $app_name | grep -v grep | awk {'print $3*100'}`
if [[ $app_cpu -gt $cpu_limit ]]; then
echo "crap"
else
echo "we're good"
fi
Keep in mind that CPU percentage is a suboptimal measurement of application health. If you have two processes running infinite loops on a single core system, no other application of the same priority will ever go over 33%, even if they're trashing around.
#!/bin/sh
PROCESS="java"
PID=`pgrep $PROCESS | tail -n 1`
CPU=`top -b -p $PID -n 1 | tail -n 1 | awk '{print $9}'`
echo $CPU
I came up with this, using top and bc.
Use it by passing in ex: ./script apache2 50 # max 50%
If there are many PIDs matching your program argument, only one will be calculated, based on how top lists them. I could have extended the script by catching them all and avergaing the percentage or something, but this will have to do.
You can also pass in a number, ./script.sh 12345 50, which will force it to use an exact PID.
#!/bin/bash
# 1: ['command\ name' or PID number(,s)] 2: MAX_CPU_PERCENT
[[ $# -ne 2 ]] && exit 1
PID_NAMES=$1
# get all PIDS as nn,nn,nn
if [[ ! "$PID_NAMES" =~ ^[0-9,]+$ ]] ; then
PIDS=$(pgrep -d ',' -x $PID_NAMES)
else
PIDS=$PID_NAMES
fi
# echo "$PIDS $MAX_CPU"
MAX_CPU="$2"
MAX_CPU="$(echo "($MAX_CPU+0.5)/1" | bc)"
LOOP=1
while [[ $LOOP -eq 1 ]] ; do
sleep 0.3s
# Depending on your 'top' version and OS you might have
# to change head and tail line-numbers
LINE="$(top -b -d 0 -n 1 -p $PIDS | head -n 8 \
| tail -n 1 | sed -r 's/[ ]+/,/g' | \
sed -r 's/^\,|\,$//')"
# If multiple processes in $PIDS, $LINE will only match\
# the most active process
CURR_PID=$(echo "$LINE" | cut -d ',' -f 1)
# calculate cpu limits
CURR_CPU_FLOAT=$(echo "$LINE"| cut -d ',' -f 9)
CURR_CPU=$(echo "($CURR_CPU_FLOAT+0.5)/1" | bc)
echo "PID $CURR_PID: $CURR_CPU""%"
if [[ $CURR_CPU -ge $MAX_CPU ]] ; then
echo "PID $CURR_PID ($PID_NAMES) went over $MAX_CPU""%"
echo "[[ $CURR_CPU""% -ge $MAX_CPU""% ]]"
LOOP=0
break
fi
done
echo "Stopped"
Erik, I used a modified version of your code to create a new script that does something similar. Hope you don't mind it.
A bash script to get the CPU usage by process
usage:
nohup ./check_proc bwengine 70 &
bwegnine is the process name we want to monitor 70 is to log only when the process is using over 70% of the CPU.
Check the logs at: /var/log/check_procs.log
The output should be like:
DATE | TOTAL CPU | CPU USAGE | Process details
Example:
03/12/14 17:11 |20.99|98| ProdPROXY-ProdProxyPA.tra
03/12/14 17:11 |20.99|100| ProdPROXY-ProdProxyPA.tra
Link to the full blog:
http://felipeferreira.net/?p=1453
It is also useful to have app_user information available to test whether the current user has the rights to kill/modify the running process. This information can be obtained along with the needed app_pid and app_cpu by using read eliminating the need for awk or any other 3rd party parser:
read app_user app_pid tmp_cpu stuff <<< \
$( ps aux | grep "$app_name" | grep -v "grep\|defunct\|${0##*/}" )
You can then get your app_cpu * 100 with:
app_cpu=$((${tmp_cpu%.*} * 100))
Note: Including defunct and ${0##*/} in grep -v prevents against multiple processes matching $app_name.
I use top to check some details. It provides a few more details like CPU time.
On Linux this would be:
top -b -n 1 | grep $app_name
On Mac, with its BSD version of top:
top -l 1 | grep $app_name

Resources