Finding number of child processes - bash

How do find the number of child processes of a bash script, from within the script itself?

To obtain the PID of the bash script you can use variable $$.
Then, to obtain its children, you can run:
bash_pid=$$
children=`ps -eo ppid | grep -w $bash_pid`
ps will return the list of parent PIDs. Then grep filters all the processes not related to the bash script's children. In order to get the number of children you can do:
num_children=`echo $children | wc -w`
Actually the number you will get will be off by 1, since ps will be a child of the bash script too. If you do not want to count the execution of ps as a child, then you can just fix that with:
let num_children=num_children-1
UPDATE: In order to avoid calling grep, the following syntax might be used (if supported by the installed version of ps):
num_children=`ps --no-headers -o pid --ppid=$$ | wc -w`

I prefer:
num_children=$(pgrep -c -P$$)
It spawns just one process, you do not have to count words or adjust the numbers of PIDs by the programs in the pipe.
Example:
~ $ echo $(pgrep -c -P$$)
0
~ $ sleep 20 &
[1] 26114
~ $ echo $(pgrep -c -P$$)
1

You can also use pgrep:
child_count=$(($(pgrep --parent $$ | wc -l) - 1))
Use pgrep --parent $$ to get a list of the children of the bash process.
Then use wc -l on the output to get the number of lines: $(pgrep --parent $$ | wc -l)
Then subtract 1 (wc -l reports 1 even when pgrep --parent $$ is empty)

If job count (instead of pid count) is enough I just came with a bash-only version:
job_list=($(jobs -p))
job_count=${#job_list[#]}

Use ps with the --ppid option to select children of the current bash process.
bash_pid=$$
child_count=$(ps -o pid= --ppid $bash_id | wc -l)
let child_count-=1 # If you don't want to count the subshell that computed the answer
(Note: this requires the Linux version of ps for --ppid. I don't know if there is an equivalent for BSD ps or not.)

You may evaluate the shell builtin command jobs like:
counter = `jobs | wc -l`

Related

How to find the number of instances of current script running in bash?

I have the below code to find out the number of instances of current script running that is running with same arg1. But looks like the script creates a subshell and executes this command which also shows up in output. What would be the better approach to find the number of instances of running script ?
$cat test.sh
#!/bin/bash
num_inst=`ps -ef | grep $0 | grep $1 | wc -l`
echo $num_inst
$ps aux | grep test.sh | grep arg1 | grep -v grep | wc -l
0
$./test.sh arg1 arg2
3
$
I am looking for a solution that matches all running instance of ./test.sh arg1 arg2 not the one with ./test.sh arg10 arg20
The reason this creates a subshell is that there's a pipeline inside the command substitution. If you run ps -ef alone in a command substitution, and then separately process the output from that, you can avoid this problem:
#!/bin/bash
all_processes=$(ps -ef)
num_inst=$(echo "$all_processes" | grep "$0" | grep -c "$1")
echo "$num_inst"
I also did a bit of cleanup on the script: double-quote all variable references to avoid weird parsing, used $() instead of backticks, and replaced grep ... | wc -l with grep -c.
You might also replace the echo "$all_processes" | ... with ... <<<"$all_processes" and maybe the two greps with a single grep -c "$0 $1":
...
num_inst=$(grep -c "$0 $1" <<<"$all_processes")
...
Modify your script like this:
#!/bin/bash
ps -ef | grep $0 | wc -l
No need to store the value in a variable, the result is printed to standard out anyway.
Now why do you get 3?
When you run a command within back ticks (fyi you should use syntax num_inst=$( COMMAND ) and not back ticks), it creates a new sub-shell to run COMMAND, then assigns the stdout text to the variable. So if you remove the use of $(), you will get your expected value of 2.
To convince yourself of that, remove the | wc -l, you will see that num_inst has 3 processes, not 2. The third one exists only for the execution of COMMAND.

pgrep process count show extra count

I've a scirpt name server.sh
#!/bin/bash
process_count=$(ps aux | grep server.sh | grep -v grep | wc -l )
echo "total process running:"
echo $process_count
... other script code
when I run script I get output as
./server.sh
total process running:
2
Why do I get process count as 2 instead of 1? I only have one script running and have also excluded grep process.
Even using pgrep -f server.sh and excluding pgrep gives 2 as process count.
My approach is to redirect the output of ps into a temp file, then grep that file. That will eliminate extra processes.
ps aux > /tmp/ps.txt
process_count=$(grep server.sh /tmp/ps.txt | wc -l)
rm /tmp/ps.txt

Bash - Two processes for one script

I have a shell script, named test.sh :
#!/bin/bash
echo "start"
ps xc | grep test.sh | grep -v grep | wc -l
vartest=`ps xc | grep test.sh | grep -v grep | wc -l `
echo $vartest
echo "end"
The output result is :
start
1
2
end
So my question is, why are there two test.sh processes running when I call ps using `` (the same happens with $()) and not when I call ps directly?
How can I get the desired result (1)?
When you start a subshell, as with the backticks, bash forks itself, then executes the command you wanted to run. You then also run a pipeline which causes all of those to be run in their own subshells, so you end up with the "extra" copy of the script that's waiting for the pipeline to finish so it can gather up the output and return that to the original script.
We'll do a little expermiment using (...) to run processes explicitly in subshells and using the pgrep command which does ps | grep "name" | grep -v grep for us, just showing us the processes that match our string:
echo "Start"
(pgrep test.sh)
(pgrep test.sh) | wc -l
(pgrep test.sh | wc -l)
echo "end"
which on a run for me produces the output:
Start
30885
1
2
end
So we can see that running pgrep test.sh in a subshell only finds the single instance of test.sh, even when that subshell is part of a pipeline itself. However, if the subshell contains a pipeline then we get the forked copy of the script waiting for the pipeline to finish

bash: differing newline count when assigning result to variable [duplicate]

This question already has answers here:
Check number of running scripts using ps
(4 answers)
Closed 6 years ago.
let's say I want to see how many copies of a program are already running. I could do something like this:
ps ax | grep -c "$0"
that command by itself produces the expected result. BUT if I attempt to assign the output to a variable, it gets incremented by one! No matter how I try it:
var=$(ps ax | grep "$0" | sed -n '$=')
var=`ps ax | grep -c "$0"`
can someone please show me the right way to capture the correct output?
it would also be great to know why this is happening..
UPDATE
after the first response from #fedorqui I realize I wasn't clear enough. let me elaborate:
I am running all three commands above in the same bash script. When I run the first one, it prints out the number 2: the program itself and the grep process with that program as an argument. when I run those same commands within variable assignments, the number 3 is stored.
please note that I am using two different methods of counting lines, grep and sed. in both cases they return 3 instead of the correct answer, 2.
here is a consolidated example to try in a test.sh file:
echo -n "without assignment: "
ps ax | grep -c "$0"
var=$(ps ax | grep "$0" | sed -n '$=')
echo "using sed method: $var"
var=`ps ax | grep -c "$0"`
echo "using grep method: $var"
the results on my debian box:
without assignment: 2
using sed method: 3
using grep method: 3
the questions again: why is this happening, and how to prevent or work around?
Quoting Siegex:
Because the grep process itself is being returned by ps.
You can either of these:
"trick" grep to not match itself by surrounding one of the search
characters in a character class [ ] which doesn't change the
functionality:
Or, in this case,
Pipe to grep -v grep, so that the process doesn't match:
var=$(ps ax | grep -v grep | grep "$0")
See an example. Here we have a process sleep:
$ sleep 20 &
[1] 5602
If we check for it in the output of ps it appears twice!
$ ps -ef| grep sleep
me 5602 5433 0 09:49 pts/2 00:00:00 sleep 20
me 5607 5433 0 09:49 pts/2 00:00:00 grep --colour=auto sleep
So we can either use a character class:
$ ps -ef| grep [s]leep
me 5602 5433 0 09:49 pts/2 00:00:00 sleep 20
Or grep out the grep process:
$ ps -ef| grep sleep | grep -v grep
me 5602 5433 0 09:49 pts/2 00:00:00 sleep 20
Command substitution itself runs in a subshell so thats one bash process
your search for bash ($0) i.e. grep -c bash also ends up in the process table at that time so thats another process (grep) containing string bash. Note that, this might not show up in the process table at the time of running, depending on how busy your system is.
And you have two (or whatever) actual bash processes (sessions) running presumably are the rest
You can use a Regex trick to get rid of the false positive i.e. grep one from count:
ps ax | grep -c "[b]ash"
It would still count the subshell while doing command substitution:
var=$(ps ax | grep -c "[b]ash")
So you need to manually remove one from this count.
Example:
$ var=$(ps ax | grep -c "bash")
$ echo $var
4
$ var=$(ps ax | grep -c "[b]ash")
$ echo $var
3
Your command counts the grep command line too.
ps ax | grep -v grep | grep -c "$0"
should omit the grep from the count

How do I count specific processes on Mac OS X?

Using a Mac, what would be the best way to count the number of instances of a particular process I am running? This is for a script I am writing to find the number of ffmpeg processes running on my machine.
Should I be using top here? ps aux|grep ffmpeg? What would be the best way to get the number?
grep -c will count occurrences:
count=`ps aux | grep -v "grep" | grep -c ffmpeg`
echo $count
ps aux | grep ffmpeg | wc -l will get you the number of processes that mention the phrase 'ffmpeg' you'll need to minus 1 on this value as ps aux | grep ffmpg is a process also.
You're looking for the program called "wc" -- "wc -l" will count lines for you.
"man wc" for details.
You can try the killall command on the Mac:
$ killall -s ffmpg
kill -TERM 20148
kill -TERM 20146
kill -TERM 20140
The -s means just list what you'd do, but don't actually kill any processes. Pipe it to wc, and you should get your result:
$ killall -s ffmpg | wc -l
3
In a shell script, you can do something like this:
num_of_processes=$(killall -s ffmpg | wc -l)
pgrep:
$ pgrep -c ffmpeg
If you don't use pgrep then mere grep might produce false positives.
To avoid it you could try -C option:
$ ps -C ffmpeg -o pid= | wc -l
Check that your ps version interprets it correctly.

Resources