3

I have been playing around with the bashrc and one of the thing I want to see at all time is my cpu usage in percentage. I decided to set this data in my PS1. The problem is that to have an accurate estimation of my CPU usage I need to do operations that require waiting for at least 0.5 seconds.

As a result of this, my new command line only displays at the end of the CPU calculation, 0.5 seconds later, which is really not acceptable. To deal with this I thought that I could maybe use a thread to do the CPU calculation and only display it at the end but I don't know how to do so.

One of the problem is that I display other information after CPU percentage so I don't know if it is even possible to delay the CPU display while still displaying the rest of the command line. I thought that maybe I could display a temporary string such as ??.?? and then replace it by the real value but I am not sure how to do so since if I type commands fast the position of the ??.?? can change (for example typing ls 5 times in a row very fast).

Maybe there is an even simpler solution to my problem such as calculating the CPU percentage in an other way ?

My CPU percentage calculating function:

function cpuf(){
    NonIdle=0;Idle=0;Total=0;TotalD=0;Idled=0
    NonIdle=$((`cat /proc/stat | awk '/^cpu / {print$2+$3+$4+$7+$8+$9}'` - $NonIdle))
    Idle=$((`cat /proc/stat | awk '/^cpu / {print$5+$6}'` - $Idle))
    sleep 0.5
    NonIdle=$((`cat /proc/stat | awk '/^cpu / {print$2+$3+$4+$7+$8+$9}'` - $NonIdle))
    Idle=$((`cat /proc/stat | awk '/^cpu / {print$5+$6}'` - $Idle))
    Total=$((Idle+NonIdle))
    CPU=$(((Total-Idle)/Total))
    echo `echo "scale=2;($Total*100-$Idle*100)/$Total" | bc -l`
}

How I call it in the bashrc:

alias cpu="cpuf"
PS1+="(\[${MAGENTA}\]CPU $(cpu)%"
PiggyGenius
  • 443
  • 2
  • 9
  • 25
  • 5
    Have you considered storing the result in a file, such as `/tmp/cpu.log`, keep the `cpuf` running as a job (by calling `cpuf &` before creating the alias), and changing `alias cpu="cat /tmp/cpu.log"` ? – pah Jul 17 '16 at 21:32
  • 2
    Why are you using `cat`? `awk` takes a filename as a parameter. The multiple child processes in your function won't do much for your CPU efficiency. – cdarke Jul 17 '16 at 22:04
  • 2
    Threadp your solution seems like a very good idea, I'll check it in the morning and post a solution if it fits me, thank you. cdarke, you're very right I will change that too. – PiggyGenius Jul 17 '16 at 22:28
  • 2
    Note that if you opt by using the file solution, you may want to keep the job out of the `.bashrc` to prevent multiple jobs doing the same thing. If you really want to start that job from the `.bashrc`, remember to check if it's already running before starting a new one. (You should also consider to use a filename such as /tmp/cpu_.log if a multi-user scenario is expected... but in this case, I really recommend a single [small] service to update the file, common to all users). – pah Jul 17 '16 at 22:36

1 Answers1

2

There is no need to reinvent the wheel here, linux already takes care of capturing system stats in /proc/loadavg. The first number is average load in the last minute across all cpus, so we just need to divide by the number of cpus, which we can determine by reading /proc/cpuinfo. Rolling this into .bashrc we get:

.bashrc

...
# My fancy prompt, adjust as you like...
FANCY_PROMPT="$GREEN\u$YELLOW@\h:$PURPLE\w$BLUE$ $RESET"

CPUS=$( grep -c bogomips /proc/cpuinfo )

_prompt_command() {
    LOAD_AVG_1_MIN=$( cut -d ' ' -f 1 /proc/loadavg )
    PERCENT=$( echo "scale=0; $LOAD_AVG_1_MIN * 100 / $CPUS" | bc -l )
    PS1="CPU $PERCENT% $FANCY_PROMPT"
    true
}

PROMPT_COMMAND="_prompt_command"

In Use:

enter image description here

SO linux /proc/loadavg

Community
  • 1
  • 1
xxfelixxx
  • 6,512
  • 3
  • 31
  • 38
  • 1
    This too suffers from a [useless use of `cat`](http://www.iki.fi/era/unix/award.html). `grep` is perfectly capable of reading `/proc/cpuinfo` itself, though you can also get rid of the `wc -l` by substituting `grep -c`; and the `cut` and `bc` combo might be more efficient if you refactored to a simple Awk script. – tripleee Jul 18 '16 at 04:54
  • Removed useless cat. – xxfelixxx Jul 18 '16 at 04:57
  • I fixed the other one; please review. – tripleee Jul 18 '16 at 05:22
  • Thanks. No more cats... – xxfelixxx Jul 18 '16 at 05:27
  • Another problematic antipattern is the use of upper case in user variables, but I think we can live with that here. – tripleee Jul 18 '16 at 05:36
  • 1
    I tried your solution and I get a very different number. When my function gives me 3.77% yours is giving me 13%. Is your way more accurate and mine off ? – PiggyGenius Jul 18 '16 at 06:40
  • 1
    Actually, after some testing the numbers are pretty close if you take into account the delay. Can you tell me if my calculation is accurate ? If it is I might implement it anyway to be able to see CPU usage change in the second rather than in 45 seconds. – PiggyGenius Jul 18 '16 at 06:52
  • 1
    And by the way, why aren't you greping the cpu cores data in /proc/cpuinfo rather than counting bogomips ? – PiggyGenius Jul 18 '16 at 07:03
  • If you grep for `cpu`, you will get lots of matches, if you grep for `bogomips`, you get one match per cpu. – xxfelixxx Jul 18 '16 at 08:03
  • Using /proc/cpuinfo, the data is delayed by up to a second. That should be good enough for most purposes. The benefit is not waiting 0.5 second for your function to return before seeing a bash prompt, as well as not overly complicating your system by introducing yet another service. – xxfelixxx Jul 18 '16 at 08:05