I learned how to calculate CPU utilization when I started to use dzen from dzens gcpubar source.

Later, I read the man proc and knew more about it:

/proc/stat

kernel/system statistics. Varies with architecture. Common entries include:

cpu 3357 0 4313 1362393

The amount of time, measured in units of USER_HZ (1/100ths of a second on most architectures, use sysconf(_SC_CLK_TCK) to obtain the right value), that the system spent in user mode, user mode with low priority (nice), system mode, and the idle task, respectively.The last value should be USER_HZ times the second entry in the uptime pseudo-file.

In Linux 2.6 this line includes three additional columns: iowait - time waiting for I/O to complete (since 2.5.41); irq - time servicing interrupts (since 2.6.0-test4); softirq - time servicing softirqs (since 2.6.0-test4).

Since Linux 2.6.11, there is an eighth column, steal - stolen time, which is the time spent in other operating systems when running in a virtualized environment

Since Linux 2.6.24, there is a ninth column, guest, whichis the time spent running a virtual CPU for guest operating systems under the control of the Linux kernel.

It said the number is measured in units of USER_HZ and I assume that also applies on columns after fourth column. The easy way is to sum of all difference of two consecutive samples of those numbers. That sum represents the processed time between two sampling time. In which, the difference of column four is the idling time. If sum minus this idling time, that is the CPU utilization, divide it by sum, then its the percentage of CPU utilization.

Here is a sample of first few lines of /proc/stat:

cpu  972478 47 394033 6711061 136593 2572 9139 0 0 0
cpu0 487126 17 199314 3294038 89582 2572 8539 0 0 0
cpu1 485352 29 194719 3417023 47011 0 599 0 0 0

If you count the columns, there is a tenth column, I have no idea what tenth column means. It wasnt mentioned in manpage.

Anyway, here is a sample code:

while :; do
    # 0 1:user 2:unice 3:sys 4:idle 5:iowait 6:irq 7:softirq 8:steal 9:guest 10:?
    ncpu=($(line</proc/stat))
    sum="${ncpu[]:1}"

    cpu_total=$((${sum// /+}))
    cpu_maxval=$((cpu_total - ocpu_total))
    cpu_val=$((cpu_maxval - (ncpu[4]-ocpu[4])))
    cpu_percentage=$((100 * cpu_val / cpu_maxval))

    ocpu=("${ncpu[]}")
    ocpu_total=$cpu_total
    echo "$cpu_percentage%"
    sleep 1
done

There are few tricky things, I used line to grab first line since I only need to know the total percentage of all process units. After that, it is fed to Bash array ncpu initialization. Pretty neat, isnt it?

Then, the code assigns sum with a string representation of ncpu of elements from second to last. The first one is "cpu", we dont need that. Replacing all spaces with plus sign, so it will read like 123+456+.... Feed it to arithmetic expansion, we get the sum of all numbers. Minus it with previous sum, that is sum of difference of two samples, cpu_maxval.

Then, minus the difference of idling time, its the utilization time, cpu_val. Let cpu_val be divided by cpu_maxval, we get the percentage. Job is done.