I am writing a wrapper application to bash scripts and want the application to keep a track of which tools/processes have been launched from user scripts. I would like to know what is the best way to determine the list of child processes that were spawned of this parent process.

I tried

  1. Periodically invoking ps command and building a process tree (like ps -ejH) but this misses out on processes that ran to completion very quickly.
  2. Using a tool like forkstat that uses the proc connector interface, but that would only run with elevated privileges. While this gives the correct data, running as sudo would not work in my case?

Any suggestions how this can be achieved?

  • What language are you writing in? – ctrl-alt-delor Nov 20 '18 at 11:18
  • Do you want to include also the "grandchildren etc.", i.e. if your script spawns process A, would children of process A be included in your tracking? – humanityANDpeace Nov 22 '18 at 9:29
  • Yes, I would need the grandchildren too. Basically the subtree rooted at my wrapper application process. – divyanshm Nov 22 '18 at 9:41
  • 1
    I hope you're aware that a) a process can get rid of its parent (escape the process tree) by fork + exit b) you can start a process inside another unrelated process of the same user with ptrace eg. gdb -p PID -batch -ex 'p system("command")'. – mosvy Nov 22 '18 at 19:29
  • Yes, and that is why I was exploring #2 -> ability to listen to a process being forked right away. – divyanshm Nov 23 '18 at 8:57

If you're using Linux, you can use strace to trace system calls used by a process. For example:

~ strace -e fork,vfork,clone,execve -fb execve -o log ./foo.sh
foo bar
~ cat log
4817  execve("./foo.sh", ["./foo.sh"], [/* 42 vars */]) = 0
4817  clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f1bb563b9d0) = 4818
4818  execve("/bin/true", ["/bin/true"], [/* 42 vars */] <detached ...>
4817  --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=4818, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
4817  clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f1bb563b9d0) = 4819
4817  clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f1bb563b9d0) = 4820
4820  execve("/bin/echo", ["/bin/echo", "foo", "bar"], [/* 42 vars */] <detached ...>
4817  --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=4820, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
4817  +++ exited with 0 +++
4819  execve("/bin/sleep", ["sleep", "1"], [/* 42 vars */] <detached ...>

You can see that the script forked off three processes (PIDs 4818, 4819, 4820) using the clone(2) system call, and the execve(2) system calls in those forked off processes show the commands executed.

  • -e fork,vfork,clone,execve limits strace output to these system calls
  • -f follows child processes
  • -b execve detaches from a process when the execve is reached, so we don't see further tracing of child processes.
  • Is it advisable to rely on strace? From what I know it takes up quite a few resources. – divyanshm Nov 23 '18 at 8:59
  • 1
    @divyanshm it only takes much resources when you don't limit the set of system calls to trace. When you instead use -e with a handful of syscalls, it'll only slow down things for the processes which use these syscalls very heavily. – Ruslan Nov 23 '18 at 9:30
  • ptrace-ing a process and its children (which is what strace(1) is doing) will still slow them down considerably -- at least 1.8x slowdown for a simple strace -qfe trace=execve -o /dev/null -p PID on a shell running /.configure && make in the bash source directory. Then there's still the problem of parsing strace's output into a tree -- that doesn't sound like a piece of cake ;-) – mosvy Nov 23 '18 at 18:00
  • @mosvy most configure scripts and makefiles are exec-heavy, of course they'll have a slowdown like Ruslan says. – muru Nov 23 '18 at 22:26
  • 1
    It's not that strace(1) is a resource hog (it isn't by any measure), but that it has to work in tandem with the processes it's tracing -- it's not doing its tracing asynchronously. The traced process stops while strace is doing its thing (peeking at arguments, etc). – mosvy Nov 23 '18 at 23:17
pstree -p `pgrep NetworkManager`

I think this is what you were looking for.

use direct pid or pgrep with process name.

-p used to print pids of children.

  • This leaves me with the need to invoke this method periodically, which reduces to me missing out on processes that ran to completion very quickly. – divyanshm Nov 23 '18 at 8:58
  • Good answer though, better than my #1 :) But there was a reason why I moved to #2 - this would have the same problem – divyanshm Nov 23 '18 at 8:58

pgrep command will be helpful. Use these commands :

Print PIDs of each forked process from process($pid) :

pgrep -P $pid

For more detailed information about each forked process use this command :

ps -fp `pgrep -P $pid`

where $pid is your process ID.


I would like to know what is the best way to determine the list of child processes that were spawned of this parent process.

Assuming that you are writing in bash (you do not say). Then you can use job control. This is off by default, when running scripts.

The bash manual says:

set [--abefhkmnptuvxBCHP] [-o option] [arg ...]
-m Monitor mode.
Job control is enabled. This option is on by default for interactive shells on systems that support it (see JOB CONTROL above). Background processes run in a separate process group and a line containing their exit status is printed upon their completion.

So add to your script set -m, the you can start commands, with command &, then latter do jobs to list jobs.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.