6

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.

user2073780
  • 63
  • 1
  • 1
  • 3

7 Answers7

12

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}'
Anton Kovalenko
  • 20,999
  • 2
  • 37
  • 69
9

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.

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • Thanks for the help everyone! as for the %CPU, we actually have a process that starts eating CPU, get's to 100-200% and it then prevents the user from signing into the computer, killing the process causes it to restart and sit at .1% for a long time. I plan on having this run this as a Launch Daemon "on demand" so it should kill the process, so users don't have to reboot 1/3-1/2 the time the screen locks. – user2073780 Feb 15 '13 at 14:32
  • What if there are multiple processes with the same name and I just need to find the one which is above the set CPU? – Sandeep C Aug 11 '18 at 15:19
  • `./killJava.sh: line 7: 2941 8715: syntax error in expression (error token is "8715") ./killJava.sh: line 8: 10 60: syntax error in expression (error token is "60")` What are these errors? – Sandeep C Aug 30 '18 at 13:54
  • @SandeepC It means there are multiple processes, which the original script did not account for – that other guy Aug 30 '18 at 16:56
  • To do something for each item in a list, use a loop – that other guy Sep 01 '18 at 22:44
3
#!/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
Hasan
  • 1,814
  • 1
  • 12
  • 14
0

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
redolent
  • 4,159
  • 5
  • 37
  • 47
0

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"
0

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

Sicco
  • 6,167
  • 5
  • 45
  • 61
Xoroz
  • 427
  • 4
  • 3
0

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.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85