3

This question may look like a duplicate, but only at first glance.
Of course, I would no longer need help in how to code a one-liner that extracts a fixed number of continuous lines (e. g. 5 in this example) from a data source, e. g. top:

$ top -b -n1 | awk 'BEGIN {printf "%23s %7s\n","cpu","mem"} NR==8,NR==12 {printf "%-16s %6s%% %6s%%\n",$12,$9,$10}'

This is even a very handy one-liner that will show the processes in the system that take most CPU, with the memory usage being printed in an additional column.

So far, so good ... however, it's not that trivial. To get this list, top is necessary and may (on low system load) show up itself as process in this list. I'd rather not want that, since these calls are done in intervals and would regularly spawn top (if only for a short while). It is known that we want to begin at line 8 (NR==8). However, what if a second top in another virtual desktop was forgotten about in a terminal which messes up the list as well? In this case, two top processes must be omitted, so the last line to process will be 14.

So to improve this output and to filter out every top line that is in there, a counter seems mandatory (perhaps a for loop that we exit with a break?). Unfortunately my attempts with a for loop and i = <number> have been fruitless so far, because it would rather print every line as many times as i indicates.

I've come up with a rather hackish solution, which works but may be unsuitable for more complex cases:

top -b -n1 | grep -v ' \btop\b$' | awk 'BEGIN {printf "%23s %7s\n","cpu","mem"} NR==8,NR==12 {printf "%-16s %6s%% %6s%%\n",$12,$9,$10}'

(Note: This may give unwanted results if the user name in the second column happens to be "top" as well)

Anyways, could I get a clue how to do that in awk please (and get rid of the grep)?
Thanks in advance.

6

This is not answering your question,
but it solves the problem you are trying to solve, in a completely different way:

The full command is this (see below for example output):

ps -o comm,%cpu,%mem --sort -%cpu -A | head -6

I will describe the parts of it:

  • using ps to have more control about the output
  • Printing only the three columns we need with -o comm,%cpu,%mem
  • Make ps sort the data internally --sort -%cpu, by CPU, reverse.
  • List all processes with -A
  • Show the header and the first 5 lines of the result by | head -6

The output is similar to the output of your first command:

$ ps -o comm,%cpu,%mem --sort -%cpu -A | head -6
COMMAND         %CPU %MEM
firefox          8.9 15.5
Xorg             1.3  5.6
parcellite       0.3  1.6
compiz           0.2  1.8
konsole          0.1  0.9

The process ps is listed in the full list - one could exclude it based on the parent PID.

If we want to exclude top processes elsewhere, we can do that based on the command name.

The -A selecting all processes would be replaced by -N ...:

ps ... -N --ppid $$ -C top

As we now need to exclude processes, we use -N to select all others processes than the ones we match.

To exclude ps, we use that it has the current interactive shell as parent process, so it will have the parent pid, PPID of the shell. The PID of the current shell is $$.
So --ppid $$ matches all child processes of the current shell, and we know that there will be only one, ps.

We want to also exclude the top processes that may run on other displays on the same machine. We do that by matching the command name with -C top.

The full command with excluding the ps process itself (and only this) and all top processes would be:

ps -o comm,%cpu,%mem --sort -%cpu -N --ppid $$ -C top | head -6
  • Very nice alternate solution as well, thanks! Might even take less resources than the top approach. – syntaxerror Sep 9 '14 at 23:51
  • 1
    Yeah; I answered the question that was asked, but I was silently wondering whether top was really the best way to get a one-time list of process information. – G-Man Sep 10 '14 at 0:02
  • Oh, it will take much less ressources of course. Should be near optimal. – Volker Siegel Sep 10 '14 at 0:07
  • @G-Man Well, top displays the stuff pre-sorted already, whilst with ps you'd normally have to pipe a sort to the line to achieve the same. Unless you know about the --sort option in ps, which I had ignored... – syntaxerror Sep 10 '14 at 0:32
5
top command | awk 'BEGIN { print header; count=5}
                   NR>=8 { if ($0 ~ /your top regex/) next;
                           print fields; if (--count == 0) exit}'

On every line starting with #8, if it matches top, ignore it. Otherwise, print the portion of it that you want. The fifth time you print a line (that doesn’t match top), exit.

  • Please stop italicising parts of your code: it makes (what is otherwise a helpful contribution) quite difficult to read. – jasonwryan Sep 9 '14 at 23:29
  • 1
    Really? You have difficulty reading italics? I'm using the convention of millions of computer manuals: things that you type literally (e.g., "if" and "next") are plain text; things that you replace with your own value (e.g., regex and fields) are italic. – G-Man Sep 9 '14 at 23:44
  • Thank you! That might already be the solution. And I have nothing against your italics, for it leaves those who ask a little chance to think themselves. :) I'd always just need these general ideas, not everything word-by-word. BTW, about the count=5 bit: very sneaky! I was trying so hard with for loops, but got nowhere (except for silly output as well as annoying and totally confusing error messages from awk) – syntaxerror Sep 9 '14 at 23:53
  • 3
    Very nice solution. As an aesthetic preference, in awk, I would probably be more inclined to write NR>=8 && !/top/ { print fields; count--; } !count { exit; }. I'm not sure if this is considered more idiomatic by professional awkers, but I personally find it so. – laindir Sep 10 '14 at 1:27
  • 1
    Caveat: Make sure you keep in mind that a \b in GNU awk (gawk) will stand for backspace, so to work correctly, the regex in my OP must be changed to / \ytop\y$/, with \y` standing for "word boundar y". This has just cost me another 15 minutes to figure out, since my regex made top keep on spitting out itself all the same, although I was sure I had filtered it out by now....BUT: this is a GNU-ism; it's not available in traditional awk implementations (neither is \b). – syntaxerror Sep 10 '14 at 17:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.