25

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.

Kusalananda
  • 333,661
Tim
  • 101,790
  • 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 '19 at 03:49

4 Answers4

30
$ 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:

Kusalananda
  • 333,661
  • Thanks. (1) What does "re-initialized" mean? (2) Could you also consider why those ways I have tried do not work? – Tim Nov 27 '18 at 13:36
  • 1
    @Tim I believe this is answered by Gilles here. Bash simply does not update $$ in subshells. – Kusalananda Nov 27 '18 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 '18 at 14:09
  • @Tim It depends on whether you, in a subshell, wants to get the process ID of the script or of the subshell. Both possibilities are provided and which is the correct one is dependent on the application. No more specific answer can be given to that. – Kusalananda Nov 27 '18 at 14:20
  • 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 '18 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 '18 at 14:57
  • When is $BASHPID introduced? It isn’t available in my Debian machine with Bash 5.0. – Franklin Yu Jun 11 '21 at 22:01
  • 1
    @FranklinYu BASHPID was introduced in release 4.0. You are either unknowingly running some other shell, or an older version of bash. – Kusalananda Sep 29 '22 at 15:09
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.

  • Thanks. By portably, you mean ...? – Tim Nov 27 '18 at 15:13
  • 1
    Should work in any POSIX shell -- eg in debian's /bin/sh (dash) and in busybox (ash). –  Nov 27 '18 at 15:15
  • Is there no more direct way? – Tim Nov 27 '18 at 15:16
  • 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. –  Nov 27 '18 at 15:18
  • do you mean your function will not work in ksh93? – Tim Nov 27 '18 at 15:19
  • 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. –  Nov 27 '18 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. – Stéphane Chazelas Nov 27 '18 at 16:01
  • Thanks. I was wondering why you changed from function to command substitution (with exec)? I am thinking about writing it into a script, so whenever I want to know the pid of a shell, I just call it ( or source it, whichever is better). – Tim Dec 01 '18 at 12:05
  • I wrote a script with content just echo $PPID. :) But your command substitution is good when I don't want to invoke a script. – Tim Dec 01 '18 at 13:04
  • 1
    I removed the old getpid function because it was pretty useless -- you could use it to display the pid fine, but if you tried to assign its output to a variable eg. with (var=$(getpid); ...), it would've set var to the pid of the subshell created for the $(...) command expansion, instead of that of the process in which the var=... assignment was executed. I've added a better getpid function which will assign the pid to a variable instead of printing it. –  Dec 02 '18 at 00:29
  • 1
    I suppose pid="${BASHPID:-$(exec sh -c 'echo "$PPID"')}" would also work. – David Anderson Oct 18 '19 at 07:14
  • (pid=$(sh -c 'echo "$PPID"'); echo "$$ $pid") seems to work equally well without the exec in ash, dash and bash. Am I missing something? – Harold Fischer Dec 31 '19 at 03:55
  • @HaroldFischer all those shells optimize $(cmd) to $(exec cmd). But I'm not sure that there's any standard guarantee for that -- better be explicit. –  Dec 31 '19 at 11:54
  • @HaroldFischer fwiw, an ERR trap + set -E seems to inhibit that optimization in bash: (set -E; trap : ERR; pid=$(sh -c 'echo "$PPID"'); echo "$pid != $BASHPID") –  Dec 31 '19 at 12:10
  • @mosvy Thanks for taking the time to reach out. Why is the exec necessary in the first place? – Harold Fischer Dec 31 '19 at 18:44
  • @mosvy Also, what is test "$1" supposed to do in test "$1" && eval "$1=\$pid"? – Harold Fischer Dec 31 '19 at 18:52
  • 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. –  Jan 05 '20 at 07:31
  • 1
    @HaroldFischer 2. test "$1" tests whether $1 is an empty string or not -- a quick and dirty way to test whether that function was given a varname argument to assign the pid to or not; using a function was not the brightest idea in the 1st place. –  Jan 05 '20 at 07:32
3

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

spawn
  • 349
  • 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 '21 at 15:11
  • Thanks, that's good to know. Then the opposite is likely true. – spawn Oct 26 '21 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 '21 at 15:48
  • great stuff. read this_pid _rest < /proc/self/stat could also be used (e.g. with dash). bash can do that 128-byte optimize then file is seekable (which is (kinda?) nice). that read syscall per byte is (in other cases) a bit "unfortunate" – Tomi Ollila Nov 06 '23 at 08:39
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.

KFL
  • 269