How to get PID from forked child process in shell script

The PID of a backgrounded child process is stored in $!, and the current process is $$:

fpfunction &
child_pid=$!     # in parent process, child's pid is $!
parent_pid=$$    # in parent process, parent's pid is $$

When in the backgrounded function, the child processes's PID is $BASHPID rather than $$, which is now the parent's PID:

fpfunction() {
    local child_pid=$BASHPID   # in child process, child's pid is $BASHPID
    local parent_pid=$$        # in child process, parent's pid is $$
    ...
}

Also for what it's worth, you can combine the looping statements into a single C-like for loop:

for ((n = 1; n < 20; ++n)); do
    echo "Hello World-- $n times"
    sleep 2
    echo "Hello World2-- $n times"
done

From the Bash manual:

!

Expands to the process ID of the most recently executed background (asynchronous) command.

i.e., use $!.


The script, child and parent PIDs can always be determined correctly in the parent script independent of the method employed to start it:

child () { :; }

child &
( child ) &
sh -c "child () { :; }; child" &

script_pid="${$}"    # PID of the running shell script
child_pid="${!}"     # last started child process
parent_pid="${PPID}" # PID of parent process

Whether the information is accesible in the child process depends on how the child process has been started.

With the function:

 child ()
 {
     sleep "$( expr "${2-1}" '*' 5 )"
     script_pid="${3-}"
     child_pid="${$}"
     parent_pid="${PPID}"
     message="CHILD ${1-}"
     test x"${script_pid}" != x"${child_pid}" || message="${message}"' UNFORTUNATELY WRONG!'
     report
     sleep 20
 }

and the asynchrounous calls:

child FUNC "${indx}" "${$}" &

( child SUB "${indx}" "${$}" ) &

sh -c "${FUNCTIONS}
child SH "${indx}" \"${$}\"" &

the output shows, that only the function invoked with an explicit sh -c has access to the correct PID/PPID information:

# --------------------------------------------------
#   :PRC:   CHILD FUNC UNFORTUNATELY WRONG!
#   :DBG:   script_pid     : [20634]
#   :DBG:   child_pid      : [20634]
#   :DBG:   parent_pid     : [10857]
F   UID   PID  PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND
0  1114 10857 10853  20   0 2756840 1942820 poll_s Ssl ?       95:15 emacs
0  1114 20634 10857  20   0   4636  1732 wait   Ss+  pts/22     0:00 sh /tmp/check_child_and_parent_pids.sh

# --------------------------------------------------
#   :PRC:   CHILD SUB UNFORTUNATELY WRONG!
#   :DBG:   script_pid     : [20634]
#   :DBG:   child_pid      : [20634]
#   :DBG:   parent_pid     : [10857]
F   UID   PID  PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND
0  1114 10857 10853  20   0 2756840 1942820 poll_s Ssl ?       95:15 emacs
0  1114 20634 10857  20   0   4636  1732 wait   Ss+  pts/22     0:00 sh /tmp/check_child_and_parent_pids.sh

# --------------------------------------------------
#   :PRC:   CHILD SH
#   :DBG:   script_pid     : [20634]
#   :DBG:   child_pid      : [20658]
#   :DBG:   parent_pid     : [20634]
F   UID   PID  PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND
0  1114 20634 10857  20   0   4636  1732 wait   Ss+  pts/22     0:00 sh /tmp/check_child_and_parent_pids.sh
0  1114 20658 20634  20   0   4636  1720 wait   S+   pts/22     0:00 sh -c show_processes () {     test -z

The complete test script is:

FUNCTIONS="$(
cat <<'EOF'
show_processes ()
{
    test -z "${1-}" || printf "%s\n" "${1}"

    _sep=
    pids_rx=
    for _pid in "${script_pid}" "${child_pid}" "${parent_pid}"
    do
    test -n "${_pid}" || continue
    pids_rx="${pids_rx}${_sep}${_pid}"
    _sep='\|'
    done

    ps axl | head -1
    ps axl | grep '^[^ ]*  *[^ ]*  *\('"${pids_rx}"'\)' | cut -c -107 | sort -k 3
}

report ()
{
    printf "%s\n" ""
    printf >&2 "# --------------------------------------------------\n"
    printf >&2 "#   "":PRC:   %s\n" "${message}"
    test -z "${script_pid}" || \
    printf >&2 "#   "":DBG:   %-${dbg_fwid-15}s: [%s]\n" "script_pid" "${script_pid}"
    test -z "${child_pid}" || \
    printf >&2 "#   "":DBG:   %-${dbg_fwid-15}s: [%s]\n" "child_pid" "${child_pid}"
    test -z "${parent_pid}" || \
    printf >&2 "#   "":DBG:   %-${dbg_fwid-15}s: [%s]\n" "parent_pid" "${parent_pid}"
    show_processes
}

child ()
{
    sleep "$( expr "${2-1}" '*' 5 )"
    script_pid="${3-}"
    child_pid="${$}"
    parent_pid="${PPID}"
    message="CHILD ${1-}"
    test x"${script_pid}" != x"${child_pid}" || message="${message}"' UNFORTUNATELY WRONG!'
    report
    sleep 20
}
EOF
)"

eval "${FUNCTIONS}"

shell_report ()
{
    script_pid="${$}"
    child_pid="${!}"
    parent_pid="${PPID}"
    sleep 1
    message="SHELL ${indx}"
    report
    indx="$( expr "${indx}" + 1 )"
}

indx=1

child FUNC "${indx}" "${$}" &
shell_report

( child SUB "${indx}" "${$}" ) &
shell_report

sh -c "${FUNCTIONS}
child SH "${indx}" \"${$}\"" &
shell_report

sleep "$( expr "${indx}" '*' 5 )"
The complete output is:

# --------------------------------------------------
#   :PRC:   SHELL 1
#   :DBG:   script_pid     : [20634]
#   :DBG:   child_pid      : [20636]
#   :DBG:   parent_pid     : [10857]
F   UID   PID  PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND
0  1114 10857 10853  20   0 2756840 1942820 poll_s Ssl ?       95:15 emacs
0  1114 20634 10857  20   0   4636  1728 wait   Ss+  pts/22     0:00 sh /tmp/check_child_and_parent_pids.sh
1  1114 20636 20634  20   0   4636   104 wait   S+   pts/22     0:00 sh /tmp/check_child_and_parent_pids.sh

# --------------------------------------------------
#   :PRC:   SHELL 2
#   :DBG:   script_pid     : [20634]
#   :DBG:   child_pid      : [20647]
#   :DBG:   parent_pid     : [10857]
F   UID   PID  PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND
0  1114 10857 10853  20   0 2756840 1942820 -   Rsl  ?         95:15 emacs
0  1114 20634 10857  20   0   4636  1732 wait   Ss+  pts/22     0:00 sh /tmp/check_child_and_parent_pids.sh
1  1114 20647 20634  20   0   4636   108 wait   S+   pts/22     0:00 sh /tmp/check_child_and_parent_pids.sh

# --------------------------------------------------
#   :PRC:   SHELL 3
#   :DBG:   script_pid     : [20634]
#   :DBG:   child_pid      : [20658]
#   :DBG:   parent_pid     : [10857]
F   UID   PID  PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND
0  1114 10857 10853  20   0 2756840 1942820 poll_s Ssl ?       95:15 emacs
0  1114 20634 10857  20   0   4636  1732 wait   Ss+  pts/22     0:00 sh /tmp/check_child_and_parent_pids.sh
0  1114 20658 20634  20   0   4636   928 wait   S+   pts/22     0:00 sh -c show_processes () {     test -z

# --------------------------------------------------
#   :PRC:   CHILD FUNC UNFORTUNATELY WRONG!
#   :DBG:   script_pid     : [20634]
#   :DBG:   child_pid      : [20634]
#   :DBG:   parent_pid     : [10857]
F   UID   PID  PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND
0  1114 10857 10853  20   0 2756840 1942820 poll_s Ssl ?       95:15 emacs
0  1114 20634 10857  20   0   4636  1732 wait   Ss+  pts/22     0:00 sh /tmp/check_child_and_parent_pids.sh

# --------------------------------------------------
#   :PRC:   CHILD SUB UNFORTUNATELY WRONG!
#   :DBG:   script_pid     : [20634]
#   :DBG:   child_pid      : [20634]
#   :DBG:   parent_pid     : [10857]
F   UID   PID  PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND
0  1114 10857 10853  20   0 2756840 1942820 poll_s Ssl ?       95:15 emacs
0  1114 20634 10857  20   0   4636  1732 wait   Ss+  pts/22     0:00 sh /tmp/check_child_and_parent_pids.sh

# --------------------------------------------------
#   :PRC:   CHILD SH
#   :DBG:   script_pid     : [20634]
#   :DBG:   child_pid      : [20658]
#   :DBG:   parent_pid     : [20634]
F   UID   PID  PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND
0  1114 20634 10857  20   0   4636  1732 wait   Ss+  pts/22     0:00 sh /tmp/check_child_and_parent_pids.sh
0  1114 20658 20634  20   0   4636  1720 wait   S+   pts/22     0:00 sh -c show_processes () {     test -z

Tags:

Linux

Shell

Pid