views:

243

answers:

6

an Unix shell script with only purpose - count the number of running processes of qmail (could be anything else). Easy thing, but there must be some bug in code:

#!/bin/bash
rows=`ps aux | grep qmail | wc -l`
echo $rows

Because

echo $rows

always shows greater number of rows (11) than if I just count rows in

ps aux | grep qmail

There are just 8 rows. Does it work this way on your system too?

+1  A: 

No, since I'm not running qmail. However, you will want to, at a bare minimum, exclude the process running your grep:

ps aux | grep qmail | grep -v grep

For debugging, you may want to do:

rows=`ps aux | grep qmail`
echo $rows >debug.input
od -xcb debug.input

(to see your input to the script in great detail) and then rewrite your script temporarily as:

#!/bin/bash
rows=`cat debug.input | wc -l`
echo $rows

That way, you can see the input and figure out what effect it's having on your code, even as you debug it.

A good debugger will eventually learn to only change one variable at a time. If your changing your code to get it working, that's the variable - don't let the input to your code change as well.

paxdiablo
why should "grep qmail" print lines containing "grep"?
Paolo Tedesco
+1 for a good point, although it should've shown up in his manual run as well.
roe
because there's an extra process called `grep qmail` that contains... `qmail`.
LiraNuna
@orsogufo: `grep qmail` (which is also present in the ps-listing) contains `qmail`, so you want to remove that line.
roe
@roe: thanks, I'm a bit rusty on linux shell scripting...
Paolo Tedesco
you are genius, that found the bug, it wasn't there at all, just my fault in the way of manually counting rows... human factor as always... thanks a lot!
peterson
Downvoting because "grep ... | grep -v ..." is inelegant. The idea of this site is to encourage the best techniques (including those which are more elegant) rather than further ensconce crufty hacks that we used frequently before we knew better.
Jim Dennis
there's also the `grep [q]mail` trick
knittl
@Jim, it may well be inelegant (I won't comment on that) but, unless you're in the very unlikely position of running qmail daemons with the word grep in their command line, it works just fine :-)
paxdiablo
@paxdiablo: is is more elegant (and uses fewer resources) to make a regexp that matches "qmail" that isn't matched by itself. [q]mail is one example (and should match as fast as just using qmail as the regexp).
Vatine
@Vatine, do you _know_ that the regex is optimised for the specific one-character character class case? _How_ do you know it uses fewer resources (a decent OS will load _one_ copy of grep into shared space). Not saying you're wrong, just that you should back up arguments with facts rather than gut feelings. And, despite the downvotes (which are supposed to represent a measurable "usefulness", not a vague and subjective "elegance"), the answer works quite well for the question - "crufty hacks" are more than good enough for a certain class of problem.
paxdiablo
@paxdiablo: A regexp engine that doesn't end up doing the same for the (self-matching) "q" as it does for the single-character character-class "[q]" is, ahem, not good. The resource consumption I referred to is processes, not RAM. Please consider all resources used (yes, process-IDs, process creation and the like is also a resource). Just for tha, I'll actually give you a downvote.
Vatine
A: 

Use

$ /sbin/pidof qmail

Martin Wickman
this does not show anything (qmail is running) and I don't know why
peterson
It should work. Try to give full path to qmail. Maybe run as root?
Martin Wickman
+2  A: 

Nowadays with linux, there is pgrep. If you have it on your system, you can skip grep -v grep

$ var=$(pgrep  bash) # or `pgrep bash | wc -l`
$ echo $var
2110 2127 2144 2161 2178 2195 2212 2229
$ set -- $var; echo ${#}
8

also, if your ps command has -C option, another way

$ ps -C bash -o pid= | wc -l

if not, you can set a character class in your grep pattern

$ ps aux|grep [q]mail | wc -l
ghostdog74
A: 

A few ways...

ps -e | grep ' [q]mail' | wc -l
ps -C qmail -opid= | wc -l
pidof qmail | tr ' ' '\n' | wc -l
pixelbeat
+2  A: 

It appears that you're counting the grep process itself and the header line that ps normally prints before its output.

I'd suggest something more like:

qprocs=$(ps auxwww | grep -c "[q]mail")

... note that GNU grep has a "-c" switch to have it print a "count" of matches rather than the lines themselves. The trick with the regular expression here is to match qmail without matching the literal string that's on the grep command line. So we take any single character in the string and wrap it in square brackets such that it is a single character "class." The regexp: [q]mail matches the string qmail without matching the string [q]mail.

Note that even with this regex you may still find some false positive matches. If you really want to be more precise then you should supply a custom output format string to your ps command (see the man pages) or you should feed it through a pipemill or you should parse the output of the ps command based on fields (using awk or cut or a while read loop). (The -o option to ps is by far the easiest among these).

Jim Dennis
A: 

pgrep is on many Linux distributions, and I imagine available for other Unices.

[dan@khorium ~]$ whatis pgrep
pgrep                (1)  - look up or signal processes based on name and other attributes
[dan@khorium ~]$ pgrep mingetty
1920
1921
1922
1923
1924

In your case, pgrep qmail | wc -l should do the trick.

Norky