42

How can I grep the PS output with the headers in place?

These two process make up an app running on my server....

root     17123 16727  0 16:25 pts/6    00:00:00 grep GMC
root     32017     1 83 May03 ?        6-22:01:17 /scripts/GMC/PNetT-5.1-SP1/PNetTNetServer.bin -tempdir /usr/local/GMC/PNetT-5.1-SP1/tmpData -D

does 6-22:01:17 mean that it's been running for 6 days? I'm tring to determine the length of how long the process has been running...

Is the 2nd column the process id? So if I do kill 32017 it'll kill the 2nd process?

Ben
  • 3,630
  • 17
  • 62
  • 93

8 Answers8

62
ps -ef | egrep "GMC|PID"

Replace the "GMC" and ps switches as needed.

Example output:

root@xxxxx:~$ ps -ef | egrep "disk|PID"

UID        PID  PPID  C STIME TTY          TIME CMD
paremh1  12501 12466  0 18:31 pts/1    00:00:00 egrep disk|PID
root     14936     1  0 Apr26 ?        00:02:11 /usr/lib/udisks/udisks-daemon
root     14937 14936  0 Apr26 ?        00:00:03 udisks-daemon: not polling any devices

ps -e selects all processes, and ps -f is full-format listing which shows the column headers.

Andy
  • 187
  • 1
  • 12
Hyppy
  • 15,458
  • 1
  • 37
  • 59
  • 10
    Would be good to add some info on 'why' this works. – Elijah Lynn Jan 29 '16 at 20:21
  • 3
    No, that's an exercise for the user. – Hyppy Jan 29 '16 at 20:23
  • @ElijahLynn It matches text in the header - in this case, PID. But you could swap it out for UID, PTIME, or anything else in the header... – Ben Creasy Feb 13 '17 at 21:51
  • 9
    So `ps -e` selects all processes, and `ps -f` is full-format listing which shows the column headers. Then we pipe the column headers and output to egrep, which is extended grep and allows the pipe `|` to have a special meaning, which is OR (this OR that). So you end up matching the PID in the column headers plus the output lines that matter. – Elijah Lynn Feb 16 '17 at 20:43
  • I had to use simple quotation marks in the egrep/grep -E command in Ubuntu 16.04, e.g.: `ps -ef | grep -E 'GMC|PID'` – Vlax Apr 03 '17 at 08:49
26

Thanks to geekosaur, I would like to use this command for your demands, rather than a separated command:

ps -ef | head -1; ps -ef | grep "your-pattern-goes-here"

The tricky is to make use of the ";" supported by the shell to chain the command.

Vic Lau
  • 361
  • 3
  • 2
  • 2
    this is cleaner and more robust than the accepted answer. It does not assert that the header has PID in it and does not add complexity to the grep string. – 7yl4r Aug 02 '17 at 15:03
  • 7
    oh wait... you've got the `ps -ef` repeated. even better is `ps -ef | { head -1 ; grep "your-pattern" ; }` – 7yl4r Aug 02 '17 at 18:52
  • @7yl4r never used that shell ability. I tried your improved command, works perfectly! Got learned, :) – Vic Lau Aug 03 '17 at 02:38
  • 3
    Found explanation about this technic, called 'Grouping Commands', for others' curiosity, see: http://www.gnu.org/software/bash/manual/html_node/Command-Grouping.html – Vic Lau Aug 03 '17 at 02:53
  • @7yl4r great technique! I was looking how to not repeat the command. The grouped command can even be piped: `ps -ef | { head -1; grep "pattern" | head -5; }`. Useful if the pattern grep-ed has a lot of results! – addicted Jun 09 '19 at 12:02
  • This is cleaner and will work for any command. – user1049109 Feb 05 '20 at 19:24
  • 1
    @7yl4r Unfortunately, this doesn't work reliably: `printf 'foo\nbar\nbar\nbar\n' | { head -1; grep bar; }` will print `foo` and nothing else. You will have to go with `printf 'foo\nbar\nbar\nbar\n' | tee >(head -1) >(sleep 0.01s; grep bar) >/dev/null`. – Stefan van den Akker Jul 13 '20 at 06:58
  • 2
    After some experimentation, it looks like `head` (GNU `head` from `coreutils 8.32`) reads the first 1024 bytes which are then no longer available to subsequent commands. To fully disprove that the construct `cmd1 | { cmd2; cmd3; }` makes a copy of the output of `cmd1` available in its entirety to `cmd2` and `cmd3`, we only need to use a command that fully consumes the output as a first command: `printf 'foo\nbar\nbaz\n' | { grep nomatch; cat - }`. The output will be empty (one would expect `cat -` to print the entire input if copies were used). – Stefan van den Akker Jul 13 '20 at 07:29
  • This should be the accepted answer. In the long run, you wouldn't know if process contains another word called "PID". – MaXi32 Sep 17 '20 at 23:34
  • @StefanvandenAkker your experimentation goes deeper about Grouping Commands, really shines! Really appreciated! – Vic Lau Jan 13 '21 at 03:45
6

Second column is the process id; 4th is when the process was created (this is usually the time your program started, but not always; consider execve() and friends); 6th is the amount of CPU time consumed. So it's been running for 8 days and used almost 7 days of CPU time, which I would consider worrisome.

Getting the header in the same invocation is tricky at best; I'd just do a separate ps | head -1. You might consider using ps's own selection methods or something like pgrep instead of grep, which isn't really designed to pass headers through.

geekosaur
  • 7,025
  • 1
  • 19
  • 19
  • What's the `83` ? – Ben May 11 '11 at 20:39
  • Current process priority, which is based on its past CPU and I/O usage and the user- or system-assigned `nice` value. Smaller numbers are higher priority. In this case, `grep` is priority 0 because it was blocked on disk reads and yielded to write its output, and `PNetTNetServer.bin` is a large number because it consistently uses up its timeslice without blocking. (Scheduling is complex and the details will depend on the exact scheduler being used.) – geekosaur May 11 '11 at 20:46
6

easier alternative: ps -ef | { head -1; grep GMC; }

replace the number with the number of lines your header is displayed on.

AllBlackt
  • 161
  • 1
  • 3
  • 2
    I like this approach but the command needs another semicolon at the end. `ps -ef | { head -1; grep GMC; }`. Also I like it in a function like the following: `function pgrep() { ps -ef | { head -1; grep $@; } }` – Brett Jan 31 '17 at 17:31
  • You must add a space between the last semicolon and "}". Otherwise it will throw a syntax error. – TheQuestioner Jun 30 '22 at 12:57
5

The egrep solution is simple and useful, but of course you depend on the header always containing 'PID' (a more than reasonable assumption, though) and the same string not ocurring elsewhere. I'm guessing this is enough for your needs, but in case someone wants an alternative there's sed.

Sed lets you just say "print the first line, then any line containing the pattern". For example:

ps auxwww | sed -n '1p; /PROCESS_NAME_TO_SEARCH/p;'

Add /sed -n/d; to filter sed itself out:

ps auxwww | sed -n '1p; /sed -n/d; /PROCESS_NAME_TO_SEARCH/p;'
Eduardo Ivanec
  • 14,531
  • 1
  • 35
  • 42
  • `/sed -n/d` is not right. Some existing command might have `sed -n`, which you wanted to print. Trick is to use `sed -n '1p; /[P]ROCESS_NAME_TO_SEARCH/p'`. ;-) Note `[]` around any character in the search string. – anishsane Jul 12 '18 at 06:08
1

you could get the pid with pgrep

pgrep PNetTNetServer

and then use ps with the pid

ps u 12345

or even combine the two into one command

ps u `pgrep PNetTNetServer`

This would show just the line you want, and include the header.

James
  • 819
  • 4
  • 10
0

This works for me at Solaris and several Linux flavors. it's pretty similar to the first:

clear ; ps aux | grep -v grep | grep -E 'COMMAND|PNetTNetServer'

It's a 2 command part:

  • a) Clear the screen
  • b) ps aux for the process
  • b.1) First grep removes grep results
  • b.2) Real value grep/search, COMMAND is because I need a field from the header and PNetTNetServer is all the command processes you are looking...

And the best thing (other example how I use it with multiple patterns) is:

clear ; ps aux | grep -v grep | grep -E 'COMMAND|du -sH|tmp_usage'

This search's a script that have tmp_usage and du command... And you can add more details every pipe. Output sample:

USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
megauser   74707  0.0  0.0 113292  1492 pts/5    S+   16:17   0:00 /bin/bash /prj/mega/scripts/tmp_usage10_mega.sh 50 /prj/mega/
megauser   74718  0.0  0.0 113292   552 pts/5    S+   16:17   0:00 /bin/bash /prj/mega/scripts/tmp_usage10_mega.sh 50 /prj/mega/
megauser   74723  0.0  0.0 108356   616 pts/5    S+   16:17   0:00 xargs du -shH
megauser  117884  0.5  0.0 112912  5452 pts/5    D+   17:22   0:01 du -shH /prj/mega/dev /prj/mega/dv2 /prj/mega/sup /prj/mega/ppr /prj/mega/ts2

I hope you like it!

Juan

0

I wrote a small Perl program that will print

  • the first line and all lines matching, if there are any matches, or
  • nothing, if there are no matches.

I most often use it like ps | 1andre GMC, but it can also take file arguments (each file provides its own header line for matches made on lines from that file).



#!/usr/bin/perl

#
# 1andre <regexp> [<file> ...]
#
#   1 -           first ({1}st)
# and -                  {and}
#  re - (lines matching) {re}gexp
#
# If no <files> are given (or "-" is given as a <file>) stdin is
# used.
#
# If any lines from each <file> match the <regexp>, print the
# first line from that <file> and all the matching lines from
# that <file>.
#

use strict;
use warnings;

if(scalar @ARGV < 1) {
  printf STDERR "usage: %s <regexp> [<file> ...]\n", $0;
  exit 1;
}

my $re = shift;
my $header;

while(<>) {
  if($. == 1) {
    $header = $_;
  } elsif(m/$re/) {
    if(defined $header) {
      print $header;
      undef $header;
    }
    print;
  }
} continue {
  # close resets $. when we get to the end of each file that <> is processing
  close ARGV if eof;
}
Chris Johnsen
  • 1,598
  • 1
  • 14
  • 18