Say hello to psgrep
Now that we've done hero searching and tally ho-ing, let's move on to simplify another recurring shell task.
I somewhat regularly find myself doing something like ps aux | grep
firefox
, to see the CPU or memory usage of a process, or to find its
pid in order to kill it. (There are other ways to accomplish each of those things, of course, but this is one way to do all three.)
The slight annoyances of this approach are, at the very least, twofold.
First, when I pipe to grep, I lose the column headers in the ps output, and don't think for a second I can actually remember what all those numbers mean without some guidance. I'm having a hard anough time interpreting them correctly with the headers.
I could pipe to grep 'firefox|COMMAND'
to go around that, but that's
ugly.
Second, when grep filters the output, it also matches its own line (since there's a "firefox" in "grep firefox".
I could pipe to grep [f]irefox
to go around that - there's no
"firefox" in "grep [f]irefox". But that's ugly, and every time you
misuse a regexp like that, God kills a kitten.
So a better answer is to let ps
do the filtering itself. The -p
option lets you specify a comma-separated list of pids. The pgrep
command, in turn, can look up processes based on their name. Its -d
option lets you specify the delimiter (say, a comma).
The new command (ignoring output format for now) becomes:
$ ps -p "$(pgrep -d, firefox)"
That's a bit tedious to type out each time, so let's make a tiny script with some added candy:
#!/bin/sh
# psgrep - ps filtered by process command lines
# Make sure we got at least one argument (the pattern)
if [ $# -eq 0 ]; then
echo "Usage: psgrep [options] PATTERN"
exit 1
fi
# Use the last argument as the pattern, and grab any preceding
# arguments to pass them on to 'ps'.
PATTERN=${!#}
ARGS=${@::$#-1}
# Get the pids matching the given pattern
PIDS=$(pgrep -f -d, "$PATTERN")
# Simple error message if there were no matching processes
if [ -z "$PIDS" ]; then
echo "No matching processes."
exit
fi
# All seems fine; let's call ps.
ps ${ARGS[@]} -p "$PIDS"
Put it in the ~/bin/
, and call it psgrep
, and voila. You can now do psgrep firefox
,
and even add your favorite ps
options, like psgrep -f firefox
or
psgrep uww firefox
or whatnot.
Note that some options will select all processes, rendering the pid
selection pretty useless. On the other hand, you could easily use that
to turn this into a replacement wrapper for ps
.
"Uhm, so what about the -C
option", you ask. "Doesn't that do pretty
much the same thing?".
Yep. Pretty much. I hadn't found it when I wrote this script, though.
Also, that's why I use the -f
switch to pgrep
- that let's you
do pattern matching on the full command line, not just the process
name. If you only need exact matching on process names, go ahead and use ps -C NAME
.
Enjoy.
Update: That -f
thing kind of brings back the seconds annoyance mentioned above, doesn't it? It does. I should blog on a coffee high. But still.
Update 2: What do you mean, "let it go"? This is starting to look like line noise, but ditching the error handling could take us back to...
#!/bin/sh
# psgrep - ps filtered by process command lines
ps ${@::$#-1} -p $(pgrep -f "${!#}" | grep -v "$$")
..which seems to do it. (grep -v "$$"
excludes the pid of the psgrep
process itself - pgrep
(confused yet?) never returns its own pid, but it happily returns the pid of psgrep
.)
I'll shut up now.