23

How can I get the pid of a subshell?

For example:

$ echo $$
16808

This doesn't work, because the original shell expands $$:

$ ( echo $$ )
16808

Why does single quoting not work? After the original shell removes the single quote, does the subshell not expand $$ in itself?

$ ( echo '$$' )
$$

Why does eval not work either? Is eval run by the subshell? Why does it give me the original shell's PID?

$ ( eval echo '$$' )
16808

Thanks.

1
  • I suggest a reopen, because the questions are essentially different in my opinion ("how to avoid $$ expansion" vs. "different pid in subshell").
    – peterh
    Jun 19, 2019 at 3:49

4 Answers 4

27
$ echo $BASHPID
37152
$ ( echo $BASHPID )
18633

From the manual:

BASHPID

Expands to the process ID of the current bash process. This differs from $$ under certain circumstances, such as subshells that do not require bash to be re-initialized.

$

Expands to the process ID of the shell. In a () subshell, it expands to the process ID of the current shell, not the subshell.

Related:

8
  • Thanks. (1) What does "re-initialized" mean? (2) Could you also consider why those ways I have tried do not work?
    – Tim
    Nov 27, 2018 at 13:36
  • 1
    @Tim I believe this is answered by Gilles here. Bash simply does not update $$ in subshells.
    – Kusalananda
    Nov 27, 2018 at 13:44
  • Do you mean I should always use $BASHPID in place of $$ in any case in bash? When shall I use which?
    – Tim
    Nov 27, 2018 at 14:09
  • 1
    If I want to get the parent pid of a subshell, that is, the pid of the invoking shell of the subshell, do I have to use $$? Can I use something else which is more predictable?
    – Tim
    Nov 27, 2018 at 14:26
  • 2
    @Tim The PID of a parent shell of a subshell can't reliably be found unless you arrange to save $BASHPID in a variable and use that in the subshell. There is $PPID, but that's the parent PID of the shell in the same sense that $$ is the PID of the shell (it's not reset in a subshell). There is no $BASHPPID variable.
    – Kusalananda
    Nov 27, 2018 at 14:57
16

In addition to bash's $BASHPID, you can do it portably with:

pid=$(exec sh -c 'echo "$PPID"')

Example:

(pid=$(exec sh -c 'echo "$PPID"'); echo "$$ $pid")

You can make it into a function:

# usage getpid [varname]
getpid(){
    pid=$(exec sh -c 'echo "$PPID"')
    test "$1" && eval "$1=\$pid"
}

Notice that some shells (eg. zsh or ksh93) do NOT start a subprocess for each subshell created with (...); in that case, $pid may be end up being the same as $$, which is just right, because that's the PID of the process getpid was called from.

18
  • 1
    Should work in any POSIX shell -- eg in debian's /bin/sh (dash) and in busybox (ash).
    – user313992
    Nov 27, 2018 at 15:15
  • 2
    No. But please do not assume that a subshell is necessarily run in a subprocess -- that is not the case in ksh93, for instance.
    – user313992
    Nov 27, 2018 at 15:18
  • 2
    It will work fine in ksh93 -- it will always return the pid of the process it was called from. It's the (...) from the example which may not spawn a separate process, as it does in bash.
    – user313992
    Nov 27, 2018 at 15:21
  • 2
    Also, some shells like zsh or yash optimise out a fork() for the last command in a subshell. They may even optimise out the fork for the subshell if it's the last command in a script so your getpid could even report the parent of $$. You could define getpid as: getpid(){ sh -c 'echo "$PPID"'; return; } to disable avoid the problem. Nov 27, 2018 at 16:01
  • 2
    @HaroldFischer 1. without the exec or without that optimization, the sh -c ... process will be a grandchild, instead of a child of the process where a $(...) command substitution is used, and $PPID will be the pid of the $(...) subshell. That's exactly what happens in the set -E + trap ERR bash example above.
    – user313992
    Jan 5, 2020 at 7:31
1

On Linux a cross-shell solution (at least dash, bash, zsh) which does not spawn a new process is

read -r this_pid < /proc/self/stat; echo ${this_pid%% *}

At least in bash and zsh we can also use space as read-delimeter:

read -d ' ' this_pid < /proc/self/stat ; echo $this_pid

See also man 5 proc section /proc/[pid]/stat

3
  • Yeah, seems so. I guess that's a nice replacement for the first one (though it's probaly less performant than the second one).
    – spawn
    Oct 26, 2021 at 15:11
  • Thanks, that's good to know. Then the opposite is likely true.
    – spawn
    Oct 26, 2021 at 15:38
  • I just checked with strace (on Debian Buster with bash 5.0). Using your read yields three read-syscalls (at most 128 bytes at a time) read -d ' ' six (indeed one byte at a time). Dash on the other hand is not cheating. IFS=' ' read .. does read one byte at a time ...
    – spawn
    Oct 26, 2021 at 15:48
0

To answer the original "why" question - why doesn't $$ expands to subshell pid, even lazily evaluated by eval?

Quoting POSIX doc:

$

Expands to the decimal process ID of the invoked shell. In a subshell (see Shell Execution Environment ), '$' shall expand to the same value as that of the current shell.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .