The What

Have you ever heard of the top command in linux. Yes, the one that lists out currently running processes with a lot of other meta information about usage. A typical top command output, when piped to a file, looks like this:

[?1h=[?25l(Btop - 16:44:50 up 1 day,  7:51,  1 user,  load average: 0.30, 0.38, 0.39(B(B
Tasks:(B 384 (Btotal,(B   1 (Brunning,(B 289 (Bsleeping,(B   0 (Bstopped,(B   0 (Bzombie(B(B
%Cpu(s):(B 10.2 (Bus,(B  3.4 (Bsy,(B  0.2 (Bni,(B 85.7 (Bid,(B  0.3 (Bwa,(B  0.0 (Bhi,(B  0.1 (Bsi,(B  0.0 (Bst(B(B
KiB Mem :(B 16262016 (Btotal,(B  7029368 (Bfree,(B  2042444 (Bused,(B  7190204 (Bbuff/cache(B(B
KiB Swap:(B        0 (Btotal,(B        0 (Bfree,(B        0 (Bused.(B 13593976 (Bavail Mem (B(B

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                  (B
(B 1527 root      20   0    4552    784    724 S   6.2  0.0   0:07.06 acpid                                                                                                    (B
(B 1985 redis     20   0   51444   3632   2564 S   6.2  0.0   0:33.44 redis-server                                                                                             (B
(B 3336 angad     20   0  533196  95444  71856 S   6.2  0.6  14:36.86 Xorg                                                                                                     (B
(B 4084 angad     20   0   57972   4380   3508 R   6.2  0.0   0:00.02 top                                                                                                      (B
(B 4729 angad     20   0  863236 195212 119392 S   6.2  1.2   3:44.26 chrome     
TLDR;

Whoa! A lot of information being thrown here, but not exactly human readable. Sometimes when I am using a tool in linux, I often spin up the OG top command to look at how much the tool is using my memory as compared to other processes. I want a clear readable format which I can use to view the result of the top command so that I can embed it wherever I like, be it a blog or an issue. Basically I want something like this:


  • top - 00:26:50 up 15:33
    • 1 user
    • load average: 0.34
    • 0.69
    • 0.84
  • Tasks: 370 total
    • 1 running
    • 279 sleeping
    • 0 stopped
    • 0 zombie
  • %Cpu(s): 5.2 us
    • 1.5 sy
    • 0.1 ni
    • 92.3 id
    • 0.8 wa
    • 0.0 hi
    • 0.1 si
    • 0.0 st
  • KiB Mem : 16262016 total
    • 9118020 free
    • 1652820 used
    • 5491176 buff/cache
  • KiB Swap:
    • 0 total
    • 0 free
    • 0 used. 14034404 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
15353 angad 20 0 57824 4240 3476 R 12.5 0.0 0:00.02 top
15312 angad 20 0 273360 34332 12680 S 6.2 0.2 0:00.24 vim
1 root 20 0 225944 9512 6544 S 0.0 0.1 0:42.51 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.07 kthreadd
4 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker/0:+
6 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 mmpercpu+
7 root 20 0 0 0 0 S 0.0 0.0 0:00.12 ksoftirqd/0
8 root 20 0 0 0 0 I 0.0 0.0 0:30.62 rcu_sched
9 root 20 0 0 0 0 I 0.0 0.0 0:00.00 rcu_bh

Aaah now this is more like it. Informative yet soothing to the eyes. Often, data visualization is limited by the perceptive short term memory load by the sheer volume of the displayed information. More times than not, we can mould the information a certain way so that it is more perceptive. Markdown comes to the rescue here!


The How

In this series, I am going to tell you how I converted the garbled top command output to a human readable form using the following steps:


What to work with

First we need to pipe the output of the top command into a file. What I like to use is the following command:

top -d 5 -n 1 -b | grep "load average" -A 15 > myfile
  • -d: This is the delay time interval (5 seconds here)
  • -n: This specifies the number of cycles to run. Since we only want a snapshot, we specify 1.
  • -b: Notice that when piped to a file, the top command prints out a lot of control characters. This can be avoided by using batch mode, which removes there characters for further processing.
  • Whatever comes after the pipe is to make sure we only get a specified number of lines in the top output (15 here). We can specify any number here, or remove this option for the full top output
  • We are using the > symbol to take the output and pipe it to a file called myfile. Use vim to open this file, and we will proceed.

Converting metadata to a list

We talked about macros in the last entry of this series. We are going to create a macro for converting the first section of the top command to a bulleted list.

top - 17:06:25 up 1 day,  8:13,  1 user,  load average: 0.25, 0.35, 0.39
Tasks: 380 total,   1 running, 287 sleeping,   0 stopped,   0 zombie
%Cpu(s):  8.8 us,  2.9 sy,  0.2 ni, 87.7 id,  0.3 wa,  0.0 hi,  0.1 si,  0.0 st
KiB Mem : 16262016 total,  7259032 free,  1811312 used,  7191672 buff/cache
KiB Swap:        0 total,        0 free,        0 used. 13827644 avail Mem 

Press q + a to start recording the macro in the a key. The following set of functions need to be performed sequentially:

  • Find all commas and add a new line instead, with a tab and a *, to specify that the item is a sub element of the list:
:%s/,/\r\t\*/g

Note that here we have used the \r symbol instead of \n as \n appends an extra character at the end of the newline. g is used to specify that this action needs to be performed on every occurrence of , in a line.

  • Cleanup the extra space in the last point:
KiB Swap:        0 total
	*        0 free
	*        0 used. 13797256 avail Mem 

For this, search for Kib, then delete the space till the 0 reaches the : and press enter to go into a new line. Insert a *. Cleanup the extra space in the last two fields also. To get this:

KiB Swap:
	*  0 total
	*  0 free
	*  0 used. 13797256 avail Mem 
  • Insert a bullet behind every field by searching and pre-pending the line with a *.

Now we have something that looks like this:

  • top - 00:26:50 up 15:33
    • 1 user
    • load average: 0.34
    • 0.69
    • 0.84
  • Tasks: 370 total
    • 1 running
    • 279 sleeping
    • 0 stopped
    • 0 zombie
  • %Cpu(s): 5.2 us
    • 1.5 sy
    • 0.1 ni
    • 92.3 id
    • 0.8 wa
    • 0.0 hi
    • 0.1 si
    • 0.0 st
  • KiB Mem : 16262016 total
    • 9118020 free
    • 1652820 used
    • 5491176 buff/cache
  • KiB Swap:
    • 0 total
    • 0 free
    • 0 used. 14034404 avail Mem

We have our first section done and recorded in macro ‘a’. To paste the contents of the macro in the current file, you can do " + a + p. You can use this output to create your custom function for performing the actions specified and adding them to a keybinding if you want.


Converting process data to a table

Under a metaphorical microscope, when we highlight all of the hidden characters, the process data looks something like this:

Alt Text

Converting this to a table format will include the following steps:

  • Search for “PID” using /PID.
  • Paste the following line below the line that contains PID:
|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
  • Since we will be replacing spaces with | to make a table, we have to make sure none of our process names have spaces. Till now I have only seen the Web Content process having a space. Simply replace it:
:%s/Web Content/Web-Content/g 
  • Go to the line with PID. Now from that line till the end of the document delete the space from each line beginning:
" The + suggests that match one or more space
" .,$ means from current line till the end of the document
:.,$s/^\s\+//
  • Convert each line ending to a |:
" $ is the line ending character
:.,$s/$/|/
  • Convert each line beginning with a |:
" ^ is the line beginning character
:.,$s/^/|/
  • Convert one or more spaces to a |:
:.,$s/\s\+/|/g

And you are good to go. The process data now looks like this:

|PID|USER|PR|NI|VIRT|RES|SHR|S|%CPU|%MEM|TIME+|COMMAND|
|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
|15353|angad|20|0|57824|4240|3476|R|12.5|0.0|0:00.02|top|
|15312|angad|20|0|273360|34332|12680|S|6.2|0.2|0:00.24|vim|
|1|root|20|0|225944|9512|6544|S|0.0|0.1|0:42.51|systemd|
|2|root|20|0|0|0|0|S|0.0|0.0|0:00.07|kthreadd|
|4|root|0|-20|0|0|0|I|0.0|0.0|0:00.00|kworker/0:+|
|6|root|0|-20|0|0|0|I|0.0|0.0|0:00.00|mm_percpu_+|
|7|root|20|0|0|0|0|S|0.0|0.0|0:00.12|ksoftirqd/0|
|8|root|20|0|0|0|0|I|0.0|0.0|0:30.62|rcu_sched|
|9|root|20|0|0|0|0|I|0.0|0.0|0:00.00|rcu_bh|

Which is exactly the format We wanted. The table will render in markdown like this:

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
15353 angad 20 0 57824 4240 3476 R 12.5 0.0 0:00.02 top
15312 angad 20 0 273360 34332 12680 S 6.2 0.2 0:00.24 vim
1 root 20 0 225944 9512 6544 S 0.0 0.1 0:42.51 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.07 kthreadd
4 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker/0:+
6 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 mmpercpu+
7 root 20 0 0 0 0 S 0.0 0.0 0:00.12 ksoftirqd/0
8 root 20 0 0 0 0 I 0.0 0.0 0:30.62 rcu_sched
9 root 20 0 0 0 0 I 0.0 0.0 0:00.00 rcu_bh

What lies ahead

You can check out the plugin I made for the same purpose here:

{% github L04DB4L4NC3R/top.vim %}