Elegantly get list of descendant processes
I would like to get a list of all the processes that descend (e.g. children, grand-children, etc) from $pid
. This is the simplest way I've come up with:
pstree -p $pid | tr "n" " " |sed "s/[^0-9]/ /g" |sed "s/ss*/ /g"
Is there any command, or any simpler way to get the full list of all descendant processes?
process ps
|
show 1 more comment
I would like to get a list of all the processes that descend (e.g. children, grand-children, etc) from $pid
. This is the simplest way I've come up with:
pstree -p $pid | tr "n" " " |sed "s/[^0-9]/ /g" |sed "s/ss*/ /g"
Is there any command, or any simpler way to get the full list of all descendant processes?
process ps
Is there a reason you need them all on one line? What are you doing with that output? I have a feeling that this is an xy problem, and you are asking the wrong question.
– jordanm
Mar 12 '13 at 17:03
I don't care about the format as long as it's clean (i.e. I don't care about'n'
delimited vs.' '
delimited). Practical use case is: a) a daemonizer script I wrote out of pure masochism (specifically, the "stop" functionality has to deal with whatever tree of processes the daemonized process spawned); and b) a timeout script that will kill whatever the timed-out process managed to create.
– STenyaK
Mar 12 '13 at 21:06
2
@STenyaK Your use cases make me think you're looking for process groups and a negative argument tokill
. See unix.stackexchange.com/questions/9480/…, unix.stackexchange.com/questions/50555/…
– Gilles
Mar 12 '13 at 21:51
@Gilles usingps ax -opid,ppid,pgrp,cmd
I see there are many processes that share the samepgrp
as the exact subtree I want to kill. (Additionally, I can't see thesetpgrp
program listed anywhere in debian stable packages: packages.debian.org/… )
– STenyaK
Mar 13 '13 at 7:42
1
Another use case: renice/ionice on a whole process tree that's eating too many resources, e.g. a large parallel build.
– Cheetah
Sep 25 '13 at 4:02
|
show 1 more comment
I would like to get a list of all the processes that descend (e.g. children, grand-children, etc) from $pid
. This is the simplest way I've come up with:
pstree -p $pid | tr "n" " " |sed "s/[^0-9]/ /g" |sed "s/ss*/ /g"
Is there any command, or any simpler way to get the full list of all descendant processes?
process ps
I would like to get a list of all the processes that descend (e.g. children, grand-children, etc) from $pid
. This is the simplest way I've come up with:
pstree -p $pid | tr "n" " " |sed "s/[^0-9]/ /g" |sed "s/ss*/ /g"
Is there any command, or any simpler way to get the full list of all descendant processes?
process ps
process ps
edited Oct 6 '15 at 7:21
perror
1,90441935
1,90441935
asked Mar 12 '13 at 13:58
STenyaK
3253410
3253410
Is there a reason you need them all on one line? What are you doing with that output? I have a feeling that this is an xy problem, and you are asking the wrong question.
– jordanm
Mar 12 '13 at 17:03
I don't care about the format as long as it's clean (i.e. I don't care about'n'
delimited vs.' '
delimited). Practical use case is: a) a daemonizer script I wrote out of pure masochism (specifically, the "stop" functionality has to deal with whatever tree of processes the daemonized process spawned); and b) a timeout script that will kill whatever the timed-out process managed to create.
– STenyaK
Mar 12 '13 at 21:06
2
@STenyaK Your use cases make me think you're looking for process groups and a negative argument tokill
. See unix.stackexchange.com/questions/9480/…, unix.stackexchange.com/questions/50555/…
– Gilles
Mar 12 '13 at 21:51
@Gilles usingps ax -opid,ppid,pgrp,cmd
I see there are many processes that share the samepgrp
as the exact subtree I want to kill. (Additionally, I can't see thesetpgrp
program listed anywhere in debian stable packages: packages.debian.org/… )
– STenyaK
Mar 13 '13 at 7:42
1
Another use case: renice/ionice on a whole process tree that's eating too many resources, e.g. a large parallel build.
– Cheetah
Sep 25 '13 at 4:02
|
show 1 more comment
Is there a reason you need them all on one line? What are you doing with that output? I have a feeling that this is an xy problem, and you are asking the wrong question.
– jordanm
Mar 12 '13 at 17:03
I don't care about the format as long as it's clean (i.e. I don't care about'n'
delimited vs.' '
delimited). Practical use case is: a) a daemonizer script I wrote out of pure masochism (specifically, the "stop" functionality has to deal with whatever tree of processes the daemonized process spawned); and b) a timeout script that will kill whatever the timed-out process managed to create.
– STenyaK
Mar 12 '13 at 21:06
2
@STenyaK Your use cases make me think you're looking for process groups and a negative argument tokill
. See unix.stackexchange.com/questions/9480/…, unix.stackexchange.com/questions/50555/…
– Gilles
Mar 12 '13 at 21:51
@Gilles usingps ax -opid,ppid,pgrp,cmd
I see there are many processes that share the samepgrp
as the exact subtree I want to kill. (Additionally, I can't see thesetpgrp
program listed anywhere in debian stable packages: packages.debian.org/… )
– STenyaK
Mar 13 '13 at 7:42
1
Another use case: renice/ionice on a whole process tree that's eating too many resources, e.g. a large parallel build.
– Cheetah
Sep 25 '13 at 4:02
Is there a reason you need them all on one line? What are you doing with that output? I have a feeling that this is an xy problem, and you are asking the wrong question.
– jordanm
Mar 12 '13 at 17:03
Is there a reason you need them all on one line? What are you doing with that output? I have a feeling that this is an xy problem, and you are asking the wrong question.
– jordanm
Mar 12 '13 at 17:03
I don't care about the format as long as it's clean (i.e. I don't care about
'n'
delimited vs. ' '
delimited). Practical use case is: a) a daemonizer script I wrote out of pure masochism (specifically, the "stop" functionality has to deal with whatever tree of processes the daemonized process spawned); and b) a timeout script that will kill whatever the timed-out process managed to create.– STenyaK
Mar 12 '13 at 21:06
I don't care about the format as long as it's clean (i.e. I don't care about
'n'
delimited vs. ' '
delimited). Practical use case is: a) a daemonizer script I wrote out of pure masochism (specifically, the "stop" functionality has to deal with whatever tree of processes the daemonized process spawned); and b) a timeout script that will kill whatever the timed-out process managed to create.– STenyaK
Mar 12 '13 at 21:06
2
2
@STenyaK Your use cases make me think you're looking for process groups and a negative argument to
kill
. See unix.stackexchange.com/questions/9480/…, unix.stackexchange.com/questions/50555/…– Gilles
Mar 12 '13 at 21:51
@STenyaK Your use cases make me think you're looking for process groups and a negative argument to
kill
. See unix.stackexchange.com/questions/9480/…, unix.stackexchange.com/questions/50555/…– Gilles
Mar 12 '13 at 21:51
@Gilles using
ps ax -opid,ppid,pgrp,cmd
I see there are many processes that share the same pgrp
as the exact subtree I want to kill. (Additionally, I can't see the setpgrp
program listed anywhere in debian stable packages: packages.debian.org/… )– STenyaK
Mar 13 '13 at 7:42
@Gilles using
ps ax -opid,ppid,pgrp,cmd
I see there are many processes that share the same pgrp
as the exact subtree I want to kill. (Additionally, I can't see the setpgrp
program listed anywhere in debian stable packages: packages.debian.org/… )– STenyaK
Mar 13 '13 at 7:42
1
1
Another use case: renice/ionice on a whole process tree that's eating too many resources, e.g. a large parallel build.
– Cheetah
Sep 25 '13 at 4:02
Another use case: renice/ionice on a whole process tree that's eating too many resources, e.g. a large parallel build.
– Cheetah
Sep 25 '13 at 4:02
|
show 1 more comment
7 Answers
7
active
oldest
votes
The following is somewhat simpler, and has the added advantage of ignoring numbers in the command names:
pstree -p $pid | grep -o '([0-9]+)' | grep -o '[0-9]+'
Or with Perl:
pstree -p $pid | perl -ne 'print "$1n" while /((d+))/g'
We're looking for numbers within parentheses so that we don't, for example, give 2 as a child process when we run across gif2png(3012)
. But if the command name contains a parenthesized number, all bets are off. There's only so far text processing can take you.
So I also think that process groups are the way to go. If you'd like to have a process run in its own process group, you can use the 'pgrphack' tool from the Debian package 'daemontools':
pgrphack my_command args
Or you could again turn to Perl:
perl -e 'setpgid or die; exec { $ARGV[0] } @ARGV;' my_command args
The only caveat here is that process groups do not nest, so if some process is creating its own process groups, its subprocesses will no longer be in the group that you created.
Child processes are arbitrary and may or may not use process groups themselves (I cannot assume anything). However your answer comes the closest to what seesm to be achievable in Linux, so I'll accept it. Thanks.
– STenyaK
Jul 17 '13 at 11:57
This was very useful !
– Michal Gallovic
Feb 11 '16 at 20:43
The pstree pipes will also include the thread ids, i.e. the IDs of the threads a $pid has started.
– maxschlepzig
Jan 21 '17 at 9:05
You can use single grep:pstree -lp | grep -Po "(?<=()d+(?=))"
– puchu
Mar 13 at 21:48
add a comment |
descendent_pids() {
pids=$(pgrep -P $1)
echo $pids
for pid in $pids; do
descendent_pids $pid
done
}
It would be only worth noting that this will work on modern shells (bash
,zsh
,fish
, and evenksh 99
), but might not work on older shells, e.g.ksh 88
– grochmal
Jul 29 '16 at 23:34
@grochmal, see my answer below for a traversal solution that does work in ksh-88.
– maxschlepzig
Jan 21 '17 at 9:06
add a comment |
The shortest version I have found that also deals correctly with commands like pop3d
:
pstree -p $pid | perl -ne 's/((d+))/print " $1"/ge'
It deals wrongly if you have commands that have weird names like: my(23)prog
.
This doesn't work for commands that are running some thread (because pstree prints those IDs, as well).
– maxschlepzig
Jan 21 '17 at 9:08
add a comment |
There is also the issue of correctness. Naively parsing the output of pstree
is problematic for several reasons:
- pstree displays PIDs and the ids of threads (names are shown in curly braces)
- a command name might contain curly braces, numbers in parentheses that make reliable parsing impossible
If you have Python and the psutil
package installed you can use this snippet to list all descendant processes:
pid=2235; python3 -c "import psutil
for c in psutil.Process($pid).children(True):
print(c.pid)"
(The psutil package is e.g. installed as a dependency of the tracer
command which is available on Fedora/CentOS.)
Alternatively, you can do an breadth-first traversal of the process tree in a bourne shell:
ps=2235; while [ "$ps" ]; do echo $ps; ps=$(echo $ps | xargs -n1 pgrep -P);
done | tail -n +2 | tr " " "n"
For computing the transitive-closure of a pid, the tail part can be omitted.
Note that the above doesn't use recursion and also runs in ksh-88.
On Linux, one can eliminate the pgrep
call and instead read the information from /proc
:
ps=2235; while [ "$ps" ]; do echo $ps ;
ps=$(for p in $ps; do cat /proc/$p/task/$p/children; done); done
| tr " " "n"' | tail -n +2
This is more efficient because we save one fork/exec for each PID and pgrep
does some additional work in each call.
add a comment |
This Linux version needs /proc and ps only. It's adapted from the last piece of @maxschlepzig's excellent answer. This version reads /proc directly from the shell instead of spawning a sub-process in a loop. It's a bit faster and arguably slightly more elegant, as this thread title requests.
#!/bin/dash
# Print all descendant pids of process pid $1
# adapted from https://unix.stackexchange.com/a/339071
ps=${1:-1}
while [ "$ps" ]; do
echo $ps
unset ps1 ps2
for p in $ps; do
read ps2 < /proc/$p/task/$p/children 2>/dev/null
ps1="$ps1 $ps2"
done
ps=$ps1
done | tr " " "n" | tail -n +2
add a comment |
In each of your two (seemingly very artificial) use cases, why do you want to kill some unfortunate process's sub-processes? How do you know better than a process when its children should live or die? This seems like poor design to me; a process should clean up after itself.
If you really do know better, then you should be forking these sub-processes, and the 'daemonized process' is apparently too dumb to be trusted to fork(2)
.
You should avoid keeping lists of child processes or grovelling through the process tree, eg by putting the child processes in a separate process group as suggested by @Gilles.
In any case, I suspect that your daemonized process would be better off creating a worker thread pool (which necessarily dies along with its containing process) than a deep tree of sub-sub-sub-processes, which something somewhere then has to clean up.
2
Both use cases are used in a continuous integration/testing environment, so they have to deal with the possibility of a bug existing in the child process/es. This bug may manifest itself as inability to properly shutdown themselves or their children, so I need a way to ensure that I can close them all in the worst case.
– STenyaK
Jul 17 '13 at 11:55
1
In that case, I'm with @Gilles and @Jander; process groups are the best way.
– AnotherSmellyGeek
Sep 13 '13 at 13:58
add a comment |
Here's a pgrep wrapper script which lets you use pgrep and get all descendants at the same time.
~/bin/pgrep_wrapper
:
#!/bin/bash
# the delimiter argument must be the first arg, otherwise it is ignored
delim=$'n'
if [ "$1" == "-d" ]; then
delim=$2
shift 2
fi
pids=
newpids=$(pgrep "$@")
status=$?
if [ $status -ne 0 ]; then
exit $status
fi
while [ "$pids" != "$newpids" ]; do
pids=$newpids
newpids=$( { echo "$pids"; pgrep -P "$(echo -n "$pids" | tr -cs '[:digit:]' ',')"; } | sort -u )
done
if [ "$delim" != $'n' ]; then
first=1
for pid in $pids; do
if [ $first -ne 1 ]; then
echo -n "$delim"
else
first=0
fi
echo -n "$pid"
done
else
echo "$pids"
fi
Invoke the same way you'd invoke normal pgrep, such as pgrep_recursive -U $USER java
to find all Java processes and sub-processes from the current user.
1
Since this is bash, I have a feeling the code used for joining the PIDs with the delimiter could be replaced with settingIFS
and using arrays ("${array[*]}
").
– muru
Apr 10 '17 at 1:22
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "106"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f67668%2felegantly-get-list-of-descendant-processes%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
7 Answers
7
active
oldest
votes
7 Answers
7
active
oldest
votes
active
oldest
votes
active
oldest
votes
The following is somewhat simpler, and has the added advantage of ignoring numbers in the command names:
pstree -p $pid | grep -o '([0-9]+)' | grep -o '[0-9]+'
Or with Perl:
pstree -p $pid | perl -ne 'print "$1n" while /((d+))/g'
We're looking for numbers within parentheses so that we don't, for example, give 2 as a child process when we run across gif2png(3012)
. But if the command name contains a parenthesized number, all bets are off. There's only so far text processing can take you.
So I also think that process groups are the way to go. If you'd like to have a process run in its own process group, you can use the 'pgrphack' tool from the Debian package 'daemontools':
pgrphack my_command args
Or you could again turn to Perl:
perl -e 'setpgid or die; exec { $ARGV[0] } @ARGV;' my_command args
The only caveat here is that process groups do not nest, so if some process is creating its own process groups, its subprocesses will no longer be in the group that you created.
Child processes are arbitrary and may or may not use process groups themselves (I cannot assume anything). However your answer comes the closest to what seesm to be achievable in Linux, so I'll accept it. Thanks.
– STenyaK
Jul 17 '13 at 11:57
This was very useful !
– Michal Gallovic
Feb 11 '16 at 20:43
The pstree pipes will also include the thread ids, i.e. the IDs of the threads a $pid has started.
– maxschlepzig
Jan 21 '17 at 9:05
You can use single grep:pstree -lp | grep -Po "(?<=()d+(?=))"
– puchu
Mar 13 at 21:48
add a comment |
The following is somewhat simpler, and has the added advantage of ignoring numbers in the command names:
pstree -p $pid | grep -o '([0-9]+)' | grep -o '[0-9]+'
Or with Perl:
pstree -p $pid | perl -ne 'print "$1n" while /((d+))/g'
We're looking for numbers within parentheses so that we don't, for example, give 2 as a child process when we run across gif2png(3012)
. But if the command name contains a parenthesized number, all bets are off. There's only so far text processing can take you.
So I also think that process groups are the way to go. If you'd like to have a process run in its own process group, you can use the 'pgrphack' tool from the Debian package 'daemontools':
pgrphack my_command args
Or you could again turn to Perl:
perl -e 'setpgid or die; exec { $ARGV[0] } @ARGV;' my_command args
The only caveat here is that process groups do not nest, so if some process is creating its own process groups, its subprocesses will no longer be in the group that you created.
Child processes are arbitrary and may or may not use process groups themselves (I cannot assume anything). However your answer comes the closest to what seesm to be achievable in Linux, so I'll accept it. Thanks.
– STenyaK
Jul 17 '13 at 11:57
This was very useful !
– Michal Gallovic
Feb 11 '16 at 20:43
The pstree pipes will also include the thread ids, i.e. the IDs of the threads a $pid has started.
– maxschlepzig
Jan 21 '17 at 9:05
You can use single grep:pstree -lp | grep -Po "(?<=()d+(?=))"
– puchu
Mar 13 at 21:48
add a comment |
The following is somewhat simpler, and has the added advantage of ignoring numbers in the command names:
pstree -p $pid | grep -o '([0-9]+)' | grep -o '[0-9]+'
Or with Perl:
pstree -p $pid | perl -ne 'print "$1n" while /((d+))/g'
We're looking for numbers within parentheses so that we don't, for example, give 2 as a child process when we run across gif2png(3012)
. But if the command name contains a parenthesized number, all bets are off. There's only so far text processing can take you.
So I also think that process groups are the way to go. If you'd like to have a process run in its own process group, you can use the 'pgrphack' tool from the Debian package 'daemontools':
pgrphack my_command args
Or you could again turn to Perl:
perl -e 'setpgid or die; exec { $ARGV[0] } @ARGV;' my_command args
The only caveat here is that process groups do not nest, so if some process is creating its own process groups, its subprocesses will no longer be in the group that you created.
The following is somewhat simpler, and has the added advantage of ignoring numbers in the command names:
pstree -p $pid | grep -o '([0-9]+)' | grep -o '[0-9]+'
Or with Perl:
pstree -p $pid | perl -ne 'print "$1n" while /((d+))/g'
We're looking for numbers within parentheses so that we don't, for example, give 2 as a child process when we run across gif2png(3012)
. But if the command name contains a parenthesized number, all bets are off. There's only so far text processing can take you.
So I also think that process groups are the way to go. If you'd like to have a process run in its own process group, you can use the 'pgrphack' tool from the Debian package 'daemontools':
pgrphack my_command args
Or you could again turn to Perl:
perl -e 'setpgid or die; exec { $ARGV[0] } @ARGV;' my_command args
The only caveat here is that process groups do not nest, so if some process is creating its own process groups, its subprocesses will no longer be in the group that you created.
answered Jul 14 '13 at 19:01
Jander
11.5k43256
11.5k43256
Child processes are arbitrary and may or may not use process groups themselves (I cannot assume anything). However your answer comes the closest to what seesm to be achievable in Linux, so I'll accept it. Thanks.
– STenyaK
Jul 17 '13 at 11:57
This was very useful !
– Michal Gallovic
Feb 11 '16 at 20:43
The pstree pipes will also include the thread ids, i.e. the IDs of the threads a $pid has started.
– maxschlepzig
Jan 21 '17 at 9:05
You can use single grep:pstree -lp | grep -Po "(?<=()d+(?=))"
– puchu
Mar 13 at 21:48
add a comment |
Child processes are arbitrary and may or may not use process groups themselves (I cannot assume anything). However your answer comes the closest to what seesm to be achievable in Linux, so I'll accept it. Thanks.
– STenyaK
Jul 17 '13 at 11:57
This was very useful !
– Michal Gallovic
Feb 11 '16 at 20:43
The pstree pipes will also include the thread ids, i.e. the IDs of the threads a $pid has started.
– maxschlepzig
Jan 21 '17 at 9:05
You can use single grep:pstree -lp | grep -Po "(?<=()d+(?=))"
– puchu
Mar 13 at 21:48
Child processes are arbitrary and may or may not use process groups themselves (I cannot assume anything). However your answer comes the closest to what seesm to be achievable in Linux, so I'll accept it. Thanks.
– STenyaK
Jul 17 '13 at 11:57
Child processes are arbitrary and may or may not use process groups themselves (I cannot assume anything). However your answer comes the closest to what seesm to be achievable in Linux, so I'll accept it. Thanks.
– STenyaK
Jul 17 '13 at 11:57
This was very useful !
– Michal Gallovic
Feb 11 '16 at 20:43
This was very useful !
– Michal Gallovic
Feb 11 '16 at 20:43
The pstree pipes will also include the thread ids, i.e. the IDs of the threads a $pid has started.
– maxschlepzig
Jan 21 '17 at 9:05
The pstree pipes will also include the thread ids, i.e. the IDs of the threads a $pid has started.
– maxschlepzig
Jan 21 '17 at 9:05
You can use single grep:
pstree -lp | grep -Po "(?<=()d+(?=))"
– puchu
Mar 13 at 21:48
You can use single grep:
pstree -lp | grep -Po "(?<=()d+(?=))"
– puchu
Mar 13 at 21:48
add a comment |
descendent_pids() {
pids=$(pgrep -P $1)
echo $pids
for pid in $pids; do
descendent_pids $pid
done
}
It would be only worth noting that this will work on modern shells (bash
,zsh
,fish
, and evenksh 99
), but might not work on older shells, e.g.ksh 88
– grochmal
Jul 29 '16 at 23:34
@grochmal, see my answer below for a traversal solution that does work in ksh-88.
– maxschlepzig
Jan 21 '17 at 9:06
add a comment |
descendent_pids() {
pids=$(pgrep -P $1)
echo $pids
for pid in $pids; do
descendent_pids $pid
done
}
It would be only worth noting that this will work on modern shells (bash
,zsh
,fish
, and evenksh 99
), but might not work on older shells, e.g.ksh 88
– grochmal
Jul 29 '16 at 23:34
@grochmal, see my answer below for a traversal solution that does work in ksh-88.
– maxschlepzig
Jan 21 '17 at 9:06
add a comment |
descendent_pids() {
pids=$(pgrep -P $1)
echo $pids
for pid in $pids; do
descendent_pids $pid
done
}
descendent_pids() {
pids=$(pgrep -P $1)
echo $pids
for pid in $pids; do
descendent_pids $pid
done
}
answered Jul 29 '16 at 23:27
Russell Davis
16314
16314
It would be only worth noting that this will work on modern shells (bash
,zsh
,fish
, and evenksh 99
), but might not work on older shells, e.g.ksh 88
– grochmal
Jul 29 '16 at 23:34
@grochmal, see my answer below for a traversal solution that does work in ksh-88.
– maxschlepzig
Jan 21 '17 at 9:06
add a comment |
It would be only worth noting that this will work on modern shells (bash
,zsh
,fish
, and evenksh 99
), but might not work on older shells, e.g.ksh 88
– grochmal
Jul 29 '16 at 23:34
@grochmal, see my answer below for a traversal solution that does work in ksh-88.
– maxschlepzig
Jan 21 '17 at 9:06
It would be only worth noting that this will work on modern shells (
bash
, zsh
, fish
, and even ksh 99
), but might not work on older shells, e.g. ksh 88
– grochmal
Jul 29 '16 at 23:34
It would be only worth noting that this will work on modern shells (
bash
, zsh
, fish
, and even ksh 99
), but might not work on older shells, e.g. ksh 88
– grochmal
Jul 29 '16 at 23:34
@grochmal, see my answer below for a traversal solution that does work in ksh-88.
– maxschlepzig
Jan 21 '17 at 9:06
@grochmal, see my answer below for a traversal solution that does work in ksh-88.
– maxschlepzig
Jan 21 '17 at 9:06
add a comment |
The shortest version I have found that also deals correctly with commands like pop3d
:
pstree -p $pid | perl -ne 's/((d+))/print " $1"/ge'
It deals wrongly if you have commands that have weird names like: my(23)prog
.
This doesn't work for commands that are running some thread (because pstree prints those IDs, as well).
– maxschlepzig
Jan 21 '17 at 9:08
add a comment |
The shortest version I have found that also deals correctly with commands like pop3d
:
pstree -p $pid | perl -ne 's/((d+))/print " $1"/ge'
It deals wrongly if you have commands that have weird names like: my(23)prog
.
This doesn't work for commands that are running some thread (because pstree prints those IDs, as well).
– maxschlepzig
Jan 21 '17 at 9:08
add a comment |
The shortest version I have found that also deals correctly with commands like pop3d
:
pstree -p $pid | perl -ne 's/((d+))/print " $1"/ge'
It deals wrongly if you have commands that have weird names like: my(23)prog
.
The shortest version I have found that also deals correctly with commands like pop3d
:
pstree -p $pid | perl -ne 's/((d+))/print " $1"/ge'
It deals wrongly if you have commands that have weird names like: my(23)prog
.
edited Jul 14 '13 at 21:13
answered Jul 14 '13 at 21:05
Ole Tange
12k1451105
12k1451105
This doesn't work for commands that are running some thread (because pstree prints those IDs, as well).
– maxschlepzig
Jan 21 '17 at 9:08
add a comment |
This doesn't work for commands that are running some thread (because pstree prints those IDs, as well).
– maxschlepzig
Jan 21 '17 at 9:08
This doesn't work for commands that are running some thread (because pstree prints those IDs, as well).
– maxschlepzig
Jan 21 '17 at 9:08
This doesn't work for commands that are running some thread (because pstree prints those IDs, as well).
– maxschlepzig
Jan 21 '17 at 9:08
add a comment |
There is also the issue of correctness. Naively parsing the output of pstree
is problematic for several reasons:
- pstree displays PIDs and the ids of threads (names are shown in curly braces)
- a command name might contain curly braces, numbers in parentheses that make reliable parsing impossible
If you have Python and the psutil
package installed you can use this snippet to list all descendant processes:
pid=2235; python3 -c "import psutil
for c in psutil.Process($pid).children(True):
print(c.pid)"
(The psutil package is e.g. installed as a dependency of the tracer
command which is available on Fedora/CentOS.)
Alternatively, you can do an breadth-first traversal of the process tree in a bourne shell:
ps=2235; while [ "$ps" ]; do echo $ps; ps=$(echo $ps | xargs -n1 pgrep -P);
done | tail -n +2 | tr " " "n"
For computing the transitive-closure of a pid, the tail part can be omitted.
Note that the above doesn't use recursion and also runs in ksh-88.
On Linux, one can eliminate the pgrep
call and instead read the information from /proc
:
ps=2235; while [ "$ps" ]; do echo $ps ;
ps=$(for p in $ps; do cat /proc/$p/task/$p/children; done); done
| tr " " "n"' | tail -n +2
This is more efficient because we save one fork/exec for each PID and pgrep
does some additional work in each call.
add a comment |
There is also the issue of correctness. Naively parsing the output of pstree
is problematic for several reasons:
- pstree displays PIDs and the ids of threads (names are shown in curly braces)
- a command name might contain curly braces, numbers in parentheses that make reliable parsing impossible
If you have Python and the psutil
package installed you can use this snippet to list all descendant processes:
pid=2235; python3 -c "import psutil
for c in psutil.Process($pid).children(True):
print(c.pid)"
(The psutil package is e.g. installed as a dependency of the tracer
command which is available on Fedora/CentOS.)
Alternatively, you can do an breadth-first traversal of the process tree in a bourne shell:
ps=2235; while [ "$ps" ]; do echo $ps; ps=$(echo $ps | xargs -n1 pgrep -P);
done | tail -n +2 | tr " " "n"
For computing the transitive-closure of a pid, the tail part can be omitted.
Note that the above doesn't use recursion and also runs in ksh-88.
On Linux, one can eliminate the pgrep
call and instead read the information from /proc
:
ps=2235; while [ "$ps" ]; do echo $ps ;
ps=$(for p in $ps; do cat /proc/$p/task/$p/children; done); done
| tr " " "n"' | tail -n +2
This is more efficient because we save one fork/exec for each PID and pgrep
does some additional work in each call.
add a comment |
There is also the issue of correctness. Naively parsing the output of pstree
is problematic for several reasons:
- pstree displays PIDs and the ids of threads (names are shown in curly braces)
- a command name might contain curly braces, numbers in parentheses that make reliable parsing impossible
If you have Python and the psutil
package installed you can use this snippet to list all descendant processes:
pid=2235; python3 -c "import psutil
for c in psutil.Process($pid).children(True):
print(c.pid)"
(The psutil package is e.g. installed as a dependency of the tracer
command which is available on Fedora/CentOS.)
Alternatively, you can do an breadth-first traversal of the process tree in a bourne shell:
ps=2235; while [ "$ps" ]; do echo $ps; ps=$(echo $ps | xargs -n1 pgrep -P);
done | tail -n +2 | tr " " "n"
For computing the transitive-closure of a pid, the tail part can be omitted.
Note that the above doesn't use recursion and also runs in ksh-88.
On Linux, one can eliminate the pgrep
call and instead read the information from /proc
:
ps=2235; while [ "$ps" ]; do echo $ps ;
ps=$(for p in $ps; do cat /proc/$p/task/$p/children; done); done
| tr " " "n"' | tail -n +2
This is more efficient because we save one fork/exec for each PID and pgrep
does some additional work in each call.
There is also the issue of correctness. Naively parsing the output of pstree
is problematic for several reasons:
- pstree displays PIDs and the ids of threads (names are shown in curly braces)
- a command name might contain curly braces, numbers in parentheses that make reliable parsing impossible
If you have Python and the psutil
package installed you can use this snippet to list all descendant processes:
pid=2235; python3 -c "import psutil
for c in psutil.Process($pid).children(True):
print(c.pid)"
(The psutil package is e.g. installed as a dependency of the tracer
command which is available on Fedora/CentOS.)
Alternatively, you can do an breadth-first traversal of the process tree in a bourne shell:
ps=2235; while [ "$ps" ]; do echo $ps; ps=$(echo $ps | xargs -n1 pgrep -P);
done | tail -n +2 | tr " " "n"
For computing the transitive-closure of a pid, the tail part can be omitted.
Note that the above doesn't use recursion and also runs in ksh-88.
On Linux, one can eliminate the pgrep
call and instead read the information from /proc
:
ps=2235; while [ "$ps" ]; do echo $ps ;
ps=$(for p in $ps; do cat /proc/$p/task/$p/children; done); done
| tr " " "n"' | tail -n +2
This is more efficient because we save one fork/exec for each PID and pgrep
does some additional work in each call.
answered Jan 21 '17 at 9:03
maxschlepzig
33.5k32135210
33.5k32135210
add a comment |
add a comment |
This Linux version needs /proc and ps only. It's adapted from the last piece of @maxschlepzig's excellent answer. This version reads /proc directly from the shell instead of spawning a sub-process in a loop. It's a bit faster and arguably slightly more elegant, as this thread title requests.
#!/bin/dash
# Print all descendant pids of process pid $1
# adapted from https://unix.stackexchange.com/a/339071
ps=${1:-1}
while [ "$ps" ]; do
echo $ps
unset ps1 ps2
for p in $ps; do
read ps2 < /proc/$p/task/$p/children 2>/dev/null
ps1="$ps1 $ps2"
done
ps=$ps1
done | tr " " "n" | tail -n +2
add a comment |
This Linux version needs /proc and ps only. It's adapted from the last piece of @maxschlepzig's excellent answer. This version reads /proc directly from the shell instead of spawning a sub-process in a loop. It's a bit faster and arguably slightly more elegant, as this thread title requests.
#!/bin/dash
# Print all descendant pids of process pid $1
# adapted from https://unix.stackexchange.com/a/339071
ps=${1:-1}
while [ "$ps" ]; do
echo $ps
unset ps1 ps2
for p in $ps; do
read ps2 < /proc/$p/task/$p/children 2>/dev/null
ps1="$ps1 $ps2"
done
ps=$ps1
done | tr " " "n" | tail -n +2
add a comment |
This Linux version needs /proc and ps only. It's adapted from the last piece of @maxschlepzig's excellent answer. This version reads /proc directly from the shell instead of spawning a sub-process in a loop. It's a bit faster and arguably slightly more elegant, as this thread title requests.
#!/bin/dash
# Print all descendant pids of process pid $1
# adapted from https://unix.stackexchange.com/a/339071
ps=${1:-1}
while [ "$ps" ]; do
echo $ps
unset ps1 ps2
for p in $ps; do
read ps2 < /proc/$p/task/$p/children 2>/dev/null
ps1="$ps1 $ps2"
done
ps=$ps1
done | tr " " "n" | tail -n +2
This Linux version needs /proc and ps only. It's adapted from the last piece of @maxschlepzig's excellent answer. This version reads /proc directly from the shell instead of spawning a sub-process in a loop. It's a bit faster and arguably slightly more elegant, as this thread title requests.
#!/bin/dash
# Print all descendant pids of process pid $1
# adapted from https://unix.stackexchange.com/a/339071
ps=${1:-1}
while [ "$ps" ]; do
echo $ps
unset ps1 ps2
for p in $ps; do
read ps2 < /proc/$p/task/$p/children 2>/dev/null
ps1="$ps1 $ps2"
done
ps=$ps1
done | tr " " "n" | tail -n +2
answered Dec 18 at 8:18
stepse
192
192
add a comment |
add a comment |
In each of your two (seemingly very artificial) use cases, why do you want to kill some unfortunate process's sub-processes? How do you know better than a process when its children should live or die? This seems like poor design to me; a process should clean up after itself.
If you really do know better, then you should be forking these sub-processes, and the 'daemonized process' is apparently too dumb to be trusted to fork(2)
.
You should avoid keeping lists of child processes or grovelling through the process tree, eg by putting the child processes in a separate process group as suggested by @Gilles.
In any case, I suspect that your daemonized process would be better off creating a worker thread pool (which necessarily dies along with its containing process) than a deep tree of sub-sub-sub-processes, which something somewhere then has to clean up.
2
Both use cases are used in a continuous integration/testing environment, so they have to deal with the possibility of a bug existing in the child process/es. This bug may manifest itself as inability to properly shutdown themselves or their children, so I need a way to ensure that I can close them all in the worst case.
– STenyaK
Jul 17 '13 at 11:55
1
In that case, I'm with @Gilles and @Jander; process groups are the best way.
– AnotherSmellyGeek
Sep 13 '13 at 13:58
add a comment |
In each of your two (seemingly very artificial) use cases, why do you want to kill some unfortunate process's sub-processes? How do you know better than a process when its children should live or die? This seems like poor design to me; a process should clean up after itself.
If you really do know better, then you should be forking these sub-processes, and the 'daemonized process' is apparently too dumb to be trusted to fork(2)
.
You should avoid keeping lists of child processes or grovelling through the process tree, eg by putting the child processes in a separate process group as suggested by @Gilles.
In any case, I suspect that your daemonized process would be better off creating a worker thread pool (which necessarily dies along with its containing process) than a deep tree of sub-sub-sub-processes, which something somewhere then has to clean up.
2
Both use cases are used in a continuous integration/testing environment, so they have to deal with the possibility of a bug existing in the child process/es. This bug may manifest itself as inability to properly shutdown themselves or their children, so I need a way to ensure that I can close them all in the worst case.
– STenyaK
Jul 17 '13 at 11:55
1
In that case, I'm with @Gilles and @Jander; process groups are the best way.
– AnotherSmellyGeek
Sep 13 '13 at 13:58
add a comment |
In each of your two (seemingly very artificial) use cases, why do you want to kill some unfortunate process's sub-processes? How do you know better than a process when its children should live or die? This seems like poor design to me; a process should clean up after itself.
If you really do know better, then you should be forking these sub-processes, and the 'daemonized process' is apparently too dumb to be trusted to fork(2)
.
You should avoid keeping lists of child processes or grovelling through the process tree, eg by putting the child processes in a separate process group as suggested by @Gilles.
In any case, I suspect that your daemonized process would be better off creating a worker thread pool (which necessarily dies along with its containing process) than a deep tree of sub-sub-sub-processes, which something somewhere then has to clean up.
In each of your two (seemingly very artificial) use cases, why do you want to kill some unfortunate process's sub-processes? How do you know better than a process when its children should live or die? This seems like poor design to me; a process should clean up after itself.
If you really do know better, then you should be forking these sub-processes, and the 'daemonized process' is apparently too dumb to be trusted to fork(2)
.
You should avoid keeping lists of child processes or grovelling through the process tree, eg by putting the child processes in a separate process group as suggested by @Gilles.
In any case, I suspect that your daemonized process would be better off creating a worker thread pool (which necessarily dies along with its containing process) than a deep tree of sub-sub-sub-processes, which something somewhere then has to clean up.
answered Jul 14 '13 at 15:54
AnotherSmellyGeek
1785
1785
2
Both use cases are used in a continuous integration/testing environment, so they have to deal with the possibility of a bug existing in the child process/es. This bug may manifest itself as inability to properly shutdown themselves or their children, so I need a way to ensure that I can close them all in the worst case.
– STenyaK
Jul 17 '13 at 11:55
1
In that case, I'm with @Gilles and @Jander; process groups are the best way.
– AnotherSmellyGeek
Sep 13 '13 at 13:58
add a comment |
2
Both use cases are used in a continuous integration/testing environment, so they have to deal with the possibility of a bug existing in the child process/es. This bug may manifest itself as inability to properly shutdown themselves or their children, so I need a way to ensure that I can close them all in the worst case.
– STenyaK
Jul 17 '13 at 11:55
1
In that case, I'm with @Gilles and @Jander; process groups are the best way.
– AnotherSmellyGeek
Sep 13 '13 at 13:58
2
2
Both use cases are used in a continuous integration/testing environment, so they have to deal with the possibility of a bug existing in the child process/es. This bug may manifest itself as inability to properly shutdown themselves or their children, so I need a way to ensure that I can close them all in the worst case.
– STenyaK
Jul 17 '13 at 11:55
Both use cases are used in a continuous integration/testing environment, so they have to deal with the possibility of a bug existing in the child process/es. This bug may manifest itself as inability to properly shutdown themselves or their children, so I need a way to ensure that I can close them all in the worst case.
– STenyaK
Jul 17 '13 at 11:55
1
1
In that case, I'm with @Gilles and @Jander; process groups are the best way.
– AnotherSmellyGeek
Sep 13 '13 at 13:58
In that case, I'm with @Gilles and @Jander; process groups are the best way.
– AnotherSmellyGeek
Sep 13 '13 at 13:58
add a comment |
Here's a pgrep wrapper script which lets you use pgrep and get all descendants at the same time.
~/bin/pgrep_wrapper
:
#!/bin/bash
# the delimiter argument must be the first arg, otherwise it is ignored
delim=$'n'
if [ "$1" == "-d" ]; then
delim=$2
shift 2
fi
pids=
newpids=$(pgrep "$@")
status=$?
if [ $status -ne 0 ]; then
exit $status
fi
while [ "$pids" != "$newpids" ]; do
pids=$newpids
newpids=$( { echo "$pids"; pgrep -P "$(echo -n "$pids" | tr -cs '[:digit:]' ',')"; } | sort -u )
done
if [ "$delim" != $'n' ]; then
first=1
for pid in $pids; do
if [ $first -ne 1 ]; then
echo -n "$delim"
else
first=0
fi
echo -n "$pid"
done
else
echo "$pids"
fi
Invoke the same way you'd invoke normal pgrep, such as pgrep_recursive -U $USER java
to find all Java processes and sub-processes from the current user.
1
Since this is bash, I have a feeling the code used for joining the PIDs with the delimiter could be replaced with settingIFS
and using arrays ("${array[*]}
").
– muru
Apr 10 '17 at 1:22
add a comment |
Here's a pgrep wrapper script which lets you use pgrep and get all descendants at the same time.
~/bin/pgrep_wrapper
:
#!/bin/bash
# the delimiter argument must be the first arg, otherwise it is ignored
delim=$'n'
if [ "$1" == "-d" ]; then
delim=$2
shift 2
fi
pids=
newpids=$(pgrep "$@")
status=$?
if [ $status -ne 0 ]; then
exit $status
fi
while [ "$pids" != "$newpids" ]; do
pids=$newpids
newpids=$( { echo "$pids"; pgrep -P "$(echo -n "$pids" | tr -cs '[:digit:]' ',')"; } | sort -u )
done
if [ "$delim" != $'n' ]; then
first=1
for pid in $pids; do
if [ $first -ne 1 ]; then
echo -n "$delim"
else
first=0
fi
echo -n "$pid"
done
else
echo "$pids"
fi
Invoke the same way you'd invoke normal pgrep, such as pgrep_recursive -U $USER java
to find all Java processes and sub-processes from the current user.
1
Since this is bash, I have a feeling the code used for joining the PIDs with the delimiter could be replaced with settingIFS
and using arrays ("${array[*]}
").
– muru
Apr 10 '17 at 1:22
add a comment |
Here's a pgrep wrapper script which lets you use pgrep and get all descendants at the same time.
~/bin/pgrep_wrapper
:
#!/bin/bash
# the delimiter argument must be the first arg, otherwise it is ignored
delim=$'n'
if [ "$1" == "-d" ]; then
delim=$2
shift 2
fi
pids=
newpids=$(pgrep "$@")
status=$?
if [ $status -ne 0 ]; then
exit $status
fi
while [ "$pids" != "$newpids" ]; do
pids=$newpids
newpids=$( { echo "$pids"; pgrep -P "$(echo -n "$pids" | tr -cs '[:digit:]' ',')"; } | sort -u )
done
if [ "$delim" != $'n' ]; then
first=1
for pid in $pids; do
if [ $first -ne 1 ]; then
echo -n "$delim"
else
first=0
fi
echo -n "$pid"
done
else
echo "$pids"
fi
Invoke the same way you'd invoke normal pgrep, such as pgrep_recursive -U $USER java
to find all Java processes and sub-processes from the current user.
Here's a pgrep wrapper script which lets you use pgrep and get all descendants at the same time.
~/bin/pgrep_wrapper
:
#!/bin/bash
# the delimiter argument must be the first arg, otherwise it is ignored
delim=$'n'
if [ "$1" == "-d" ]; then
delim=$2
shift 2
fi
pids=
newpids=$(pgrep "$@")
status=$?
if [ $status -ne 0 ]; then
exit $status
fi
while [ "$pids" != "$newpids" ]; do
pids=$newpids
newpids=$( { echo "$pids"; pgrep -P "$(echo -n "$pids" | tr -cs '[:digit:]' ',')"; } | sort -u )
done
if [ "$delim" != $'n' ]; then
first=1
for pid in $pids; do
if [ $first -ne 1 ]; then
echo -n "$delim"
else
first=0
fi
echo -n "$pid"
done
else
echo "$pids"
fi
Invoke the same way you'd invoke normal pgrep, such as pgrep_recursive -U $USER java
to find all Java processes and sub-processes from the current user.
edited Apr 10 '17 at 1:22
muru
1
1
answered Apr 10 '17 at 1:01
zeroimpl
1011
1011
1
Since this is bash, I have a feeling the code used for joining the PIDs with the delimiter could be replaced with settingIFS
and using arrays ("${array[*]}
").
– muru
Apr 10 '17 at 1:22
add a comment |
1
Since this is bash, I have a feeling the code used for joining the PIDs with the delimiter could be replaced with settingIFS
and using arrays ("${array[*]}
").
– muru
Apr 10 '17 at 1:22
1
1
Since this is bash, I have a feeling the code used for joining the PIDs with the delimiter could be replaced with setting
IFS
and using arrays ( "${array[*]}
").– muru
Apr 10 '17 at 1:22
Since this is bash, I have a feeling the code used for joining the PIDs with the delimiter could be replaced with setting
IFS
and using arrays ( "${array[*]}
").– muru
Apr 10 '17 at 1:22
add a comment |
Thanks for contributing an answer to Unix & Linux Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f67668%2felegantly-get-list-of-descendant-processes%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Is there a reason you need them all on one line? What are you doing with that output? I have a feeling that this is an xy problem, and you are asking the wrong question.
– jordanm
Mar 12 '13 at 17:03
I don't care about the format as long as it's clean (i.e. I don't care about
'n'
delimited vs.' '
delimited). Practical use case is: a) a daemonizer script I wrote out of pure masochism (specifically, the "stop" functionality has to deal with whatever tree of processes the daemonized process spawned); and b) a timeout script that will kill whatever the timed-out process managed to create.– STenyaK
Mar 12 '13 at 21:06
2
@STenyaK Your use cases make me think you're looking for process groups and a negative argument to
kill
. See unix.stackexchange.com/questions/9480/…, unix.stackexchange.com/questions/50555/…– Gilles
Mar 12 '13 at 21:51
@Gilles using
ps ax -opid,ppid,pgrp,cmd
I see there are many processes that share the samepgrp
as the exact subtree I want to kill. (Additionally, I can't see thesetpgrp
program listed anywhere in debian stable packages: packages.debian.org/… )– STenyaK
Mar 13 '13 at 7:42
1
Another use case: renice/ionice on a whole process tree that's eating too many resources, e.g. a large parallel build.
– Cheetah
Sep 25 '13 at 4:02