How to set process group of a shell script ? Also I want all the child process to be in the same process group
I expect something similar to setpgid() in C.
Here's a late synthesis, taken from several other good answers here, if your intention is to cleanup any spawned subshell processes (even if the script itself is not directly launched from an interactive shell, but from another process, and therefore doesn't automatically becomes its own process group leader), relaunching the current script as a new process group leader if necessary.
# First, obtain the current PGID, by parsing the output of "ps".
pgid=$(($(ps -o pgid= -p "$$")))
# Check if we're already the process group leader; if not, re-launch ourselves.
# Use setsid instead of set -m (...) to avoid having another subshell in between. This helps that the trap gets executed when the script is killed.
[ $$ -eq $pgid ] || exec setsid --wait "${BASH_SOURCE[0]}" "$@"
# Kill any subshell processes when the script exits.
trap "kill -- -$pgid" EXIT
# Note: If the script only starts background jobs, and that's all you care about, you can replace all of the above with this simple trap:
#trap "jobs -p | xargs kill --" EXIT # Kill remaining jobs when the script exits.
Another complication is introduced when one script that does subshell cleanup is invoked by another such script. The process group leadership does not nest; once a script assumes leadership, its lifetime is not controlled any longer by a parent script, so when the parent script gets interrupted or killed, the nested script will linger on. That's not what the user usually wants.
The following script fragments extend the above implementation with a cooperation model, so that only the toplevel script assumes the process group leadership, indicating this to subshells by exporting $PGID. If a subshell finds an existing leader, it will not assume leadership itself, and limits its own cleanup tasks to remaining jobs. Other subshells will only be killed once the toplevel script exits. (So this cooperation model works best when one script only invokes one or only few other scripts.)
if [ -z "$PGID" ]; then # No parent script has become the process group leader yet.
pgid=$(($(ps -o pgid= -p "$$"))) # By defining this, we'll be killing subshell processes of this process group when we're done or interrupted. Any children with the same ambition will defer to us.
if [ $$ -eq $pgid ]; then
export PGID=$pgid # We are (already / after setsid) in our own process group, announce our leadership to any children, so that they don't become leaders themselves and thereby decouple themselves from our lifetime control.
else
exec setsid --wait "${BASH_SOURCE[0]}" "$@" # Use setsid instead of set -m (...) to avoid having another subshell in between.
fi
fi
if [ -n "$pgid" ]; then
trap "kill -- -$pgid" EXIT # If we're the leader, kill subshell processes when the script exits.
else
trap "jobs -p | xargs kill --" EXIT # Someone else is the leader; killing remaining jobs is all we can do here.
fi