linux shell scripting kiddie's question - linux

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?

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

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).

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.

Use
$ /sbin/pidof qmail

A few ways...
ps -e | grep ' [q]mail' | wc -l
ps -C qmail -opid= | wc -l
pidof qmail | tr ' ' '\n' | wc -l

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.

Related

cat: pid.txt: No such file or directory

I have a problem with cat. I want to write script doing the same thing as ps -e. In pid.txt i have PID of running processes.
ls /proc/ | grep -o "[0-9]" | sort -h > pid.txt
Then i want use $line like a part of path to cmdline for evry PID.
cat pid.txt | while read line; do cat /proc/$line/cmdline; done
i try for loop too
for id in 'ls /proc/ | grep -o "[0-9]\+" | sort -h'; do
cat /proc/$id/cmdline;
done
Don't know what i'm doing wrong. Thanks in advance.
I think what you're after is this - there were a few flaws with all of your approaches (or did you really just want to look at process with a single-digit PID?):
for pid in $(ls /proc/ | grep -E '^[0-9]+$'|sort -h); do cat /proc/${pid}/cmdline; tr '\x00' '\n'; done
You seem to be in a different current directory when running cat pid.txt... command compared to when you ran your ls... command. Run both your commands on the same terminal window, or use absolute path, like /path/to/pid.txt
Other than your error, you might wanna remove -o from your grep command as it gives you 1 digit for a matching pid. For example, you get 2 when pid is 423. #Roadowl also pointed that already.

Why doesn't the pipeline take effect in bash in Linux?

Here is to count the number of sessions by the same login user.
I could run the direct command if I know the specific user name, such as usera, as the following:
who | grep usera | wc -l
And if I don't know the current user, I need to user parameter.
But the following codes don't work:
currentuser=`whoami`
sessionnumber=`who | grep "$currentuser" | wc -l`
What's the error?
Thanks!
Grep has the -c flag so the wc -l plus the additional pipe is not needed.
who | grep -c -- "$USER"
"$LOGNAME" is also an option instead of "$USER", which one is bash specific? I don't know, all I know is that they are both on Linux and FreeBSD system. The -- is just a habit just in case the user starts with a dash grep will not interpret it as an option.
sessionnumber=`who | grep "$currentuser" | wc -l`
You are assigning the result of the who | ... command to a variable and to see its value you can use echo $sessionnumber
Looks like you are confused about parameters and variables.
What you are trying to get is likely
who | grep $(whoami) | wc -l
The $() is equivalent to the backticks you used.
When you write
sessionnumber=``
this will run whatever is within the backticks and save the output to a variable. You can then access the variable using the dollar notation:
echo "$sessionnumber"

How to extract field from command result in different versions of linux?

I was trying to extract a process size using the command:
size=`ps -eo vsz,pid | grep $pid | cut -'d' -f1`
However, this appeared to only work on some computers but not all. So on the ones where it wasn't working, I tried:
size=`ps -eo vsz,pid | grep $pid | awk '{print $1}'`
however, now this didn't work on the computers where the first command worked.
What I mean by "working" and "not working" is that sometimes:
echo "|$size|"
Will return something like:
|8762348
9835|
And thus the following returns an arithmetic error:
(( $size > $threshold ))
because of the newline or carriage return characters stored in $size. Is there a way to reliable extract simply the first field across different versions of linux?
First you ask ps to displat info for all processes, next you try to select 1 of them.
Your command had problems on some computers, and you tagged the question with both ksh and linux, so I am not sure what command can be used best in your case:
size=$(ps -q ${pid} -o vsz --no-headers)
# or
size=$(ps -p ${pid} -o vsz | grep -v "VSZ")

grep a variable containing special characters

i'm new to bash scripting and i have to determine if a process is running in a linux environment.
Actually i use the follow command to do the job:
#ps -ef | awk '{print substr($0, index($0,$8))}' | grep -v grep | grep -w -F $PROCESSNAME
where
awk '{print substr($0, index($0,$8))}'
allow me to ignore UID PID PPID C STIME TTY TIME fields and
grep -v grep
allow me to ignore the row that contains the command itself. So at this point i have a list of all processes running on the system.
Finally:
grep -w -F $PROCESSNAME
read a variable which contains the name of the process that i want to check.
For what i understand the full command should return only the row that has the exact value of $PROCESSNAME
Actually this doesn't works correctly for processes that follow the pattern "[processname]", and probably also for other patterns.
For example to simplify, if i have a running process named "[vmmemctl]" and i run:
#ps -ef | grep -v grep | grep -w -F "vmmemctl]"
it actually returns a result:
#root 615 2 0 Feb26 ? 00:01:00 [vmmemctl]
but the actual process name in the command is different from the process name in the result.
What is the correct command that doesn't have this behavior?
Thank you
awk to the rescue!
ps -ef | awk '$8=="[command]"{NF=8;print}'
or
ps -ef | awk -v c="vmmemctl]" '$8==c{NF=8;print}'
note that this is for an exact match not pattern.
since this is an exact match the command can have spaces and other special chars in it (it's not pattern match but string equality). Using your variable name it will look like this
ps -ef | awk -v c="$PROCESSNAME" '$8==c{NF=8;print}'

How to grep both header and pattern with grep

I need to grep both header and also particular pattern only using grep,
eg
for command "ps"
output
PID TTY TIME CMD
10280 pts/16 00:00:00 ps
32463 pts/16 00:00:00 bash
how can i grep both header and pattern like 32463 so output should be
PID TTY TIME CMD
32463 pts/16 00:00:00 bash
And One thing is that solution should be general that means it should be applicable to all commands that have headers
Try this:
ps | head -1; ps | grep bash
Like this:
ps | ( read -r head; printf '%s\n' "$head"; grep bash )
This generalizes to other commands, such as
( read -r head; printf '%s\n' "$head"; sort -k4n ) <input.csv >input-sorted-4n.csv
You could encapsulate this into a script called keepheader:
#!/bin/sh
read -r head
printf '%s\n' "$head"
exec "$#"
Use like
ps | keepheader grep bash
keepheader sort -k4n <input.csv >input-sorted-4n.csv
or maybe even
keepheader keepheader grep foo <<HERE
Header with underlines
------ ---- ----------
Cat food Whiskas
Mouse bait Cheese
HERE
(Actually maybe make the script accept an optiinal numeric parameter to specify how many header lines to keep; I leave this as an exercise for the reader.)
I suggest sed:
sed -n "1p;/$pattern/p"
how can i grep both header and pattern
You could try this
ps | grep -e 'PID\|32463'
solution should be general that means it should be applicable to all commands that have headers
This requirement is almost impossible to satisfy by grep, because different commands have different headers, it is impossible to assign a regular expression to match all of them.
But you could use the following command to achieve your goal:
command | perl -e 'while(<STDIN>) { print if $. == 1 or m/$ARGV[0]/ }' pattern
If it is too cumbersome for daily use, you can put it in a custom script, such as my-grep, and put that script in your $PATH, then you can use that script like a normal command:
command | my-grep pattern
With out using grep you can get this output for ps option
$ps -p 32463
-p Select by PID.
This selects the processes whose process ID numbers appear in pidlist. Identical to p and --pid.

Resources