33

I know in bash we can create subshells using round parenthesis ( and ). As per bash man page:

(list) list is executed in a subshell environment 

Also to get the current process id we use:

echo $$ 

Now my question is how to get process id of a subshell created using ( and ) on command line?

If I use this:

echo $$; ( echo $$; ) 

I will get the parent shell's process id printed twice on stdout since $$ gets expanded even before subshell is created. So how to really force the lazy expansion?

[Solution should work on Mac as well not just Linux]

Update:

Suggested linked answer doesn't work since echo $BASHPID does not work on my Mac and returns blank.

9
  • 4
    I believe BASHPID requires Bash 4, and OS X ships with Bash 3.2 Commented Feb 2, 2012 at 23:28
  • 1
    @anubhava: True, but we (the SO community) didn't know that until you edited ;-) Commented Feb 2, 2012 at 23:29
  • 1
    Yes, but there is no indication in the other question that the solution provided there doesn't work on a Mac. That's the key point that makes this not a duplicate. Commented Feb 2, 2012 at 23:37
  • 1
    Interestingly, the reason $$ does not work in the subshell is not exactly what you have indicated in your question. If the problem were just that $$ is being expanded before the subshell code is executed, you could easily solve this by doing (eval echo '$$') but in fact, that doesn't work either. Accorinding to the very last footnote on tldp.org/LDP/abs/html/internalvariables.html, $$ simply returns the pid of the top level, period. It doesn't indicate why, though. I don't know the answer to that question. Commented Feb 3, 2012 at 17:40
  • 1
    NB that if you just want to get a sub process PID (not one from within parentheses) that's easy and available: serverfault.com/questions/205498/… Commented Feb 4, 2015 at 18:57

7 Answers 7

19

Thanks to all of you for spending your valuable time in finding answer to my question here.

However I am now answering my own question since I've found a hack way to get this pid on bash ver < 4 (will work on all the versions though). Here is the command:

echo $$; ( F='/tmp/myps'; [ ! -f $F ] && echo 'echo $PPID' > $F; ) 

It prints:

5642 13715 

Where 13715 is the pid of the subshell. To test this when I do:

echo $$; ( F='/tmp/myps'; [ ! -f $F ] && echo 'echo $PPID' > $F; bash $F; ps; ) 

I get this:

5642 13773 PID TT STAT TIME COMMAND 5642 s001 S 0:02.07 -bash 13773 s001 S+ 0:00.00 -bash 

Telling me that 13773 is indeed the pid of the subshell.

Note: I reverted back to my original solution since as @ChrisDodd commented that echo $$; ( bash -c 'echo $PPID'; ) doesn't work Linux. Above solution of mine works both on Mac and Linux.

Sign up to request clarification or add additional context in comments.

6 Comments

sh -c 'echo $PPID' is a shorter way to do the same
One oddity: when I run echo $$; ( bash -c 'echo $PPID'; ) on my computer (Linux), it prints the same pid twice...
@ChrisDodd the command group isn't run in a subshell as its command list contains only one command (this is an undocumented optimisation). Use echo $$; (:; bash -c 'echo $PPID') to force a subshell.
@ChrisDodd If you tried it from shell then it works as expected. Try this to see different values: ( echo $$; ( sh -c 'echo $PPID'; ) ) it works for me under ubuntu 12.04 as well as OSX 10.9.2
Personally this worked for me echo $$; (echo 'echo $PPID' | bash) just another variation of the above.
|
8

Unfortunately there's no easy way to do this prior to bash version 4, when $BASHPID was introduced. One thing you can do is to write a tiny program that prints its parent PID:

int main() { printf("%d\n", getppid()); return 0; } 

If you compile that as ppid and put it in your path, you can call it, eg:

$ (echo $$; ppid) 2139 29519 $ (x=$(ppid); echo $x) 29521 

One oddness I noticed, however, is that if you write

$ (ppid) 

it doesn't seem to actually run it in a subshell -- you need at least two commands inside the parentheses for bash to actually run them in a subshell.

Comments

5

You can do :

$ ( your_action ) & [1] 44012 

And find subprocess' PID like that :

$ echo "The sub PID : $!" The Sub PID : 44012 

$! returns the last job in background's PID. (see this manual)

1 Comment

Good attempt but it gets sub process's id only in next command and outside the sub shell.
2

Use homebrew to install pgrep on the Mac: brew install pgrep

Check out Link to install Homebrew.

1 Comment

Sorry for posting as an answer to the original unrelated question, but I could not comment on the previous post.
1

You can use the ppid of the parent by echoing out the BASHPID of the parent when you first enter the shell, then you background the process and can look up the pid via ppid using the parent pid.

E.g. To get the pid of a sleep 555 command backgrounded within a subshell:

(echo "$BASHPID" > /tmp/_tmp_pid_ && sleep 555 &) && ps -ho pid --ppid=$(< /tmp/_tmp_pid_)

Comments

1

I tend to want the pid in a variable, not printed to stdout:

$ ( pid=$(bash -c 'echo $PPID'); ps -ef | grep $pid ) 502 6908 6895 0 5:50PM ttys001 0:00.00 bash 0 6910 6908 0 5:50PM ttys001 0:00.01 ps -ef 502 6911 6908 0 5:50PM ttys001 0:00.01 grep 6908 

and more pertinently for my current use:

$ ( ( pid=$(bash -c 'echo $PPID'); ps -ef | grep $pid ) & ) & sleep 1 [1] 6958 502 6960 1 0 5:52PM ttys001 0:00.00 bash 0 6962 6960 0 5:52PM ttys001 0:00.01 ps -ef 502 6963 6960 0 5:52PM ttys001 0:00.01 grep 6960 

Works on MacOS bash GNU bash, version 3.2.57(1)-release (arm64-apple-darwin24) Copyright (C) 2007 Free Software Foundation, Inc.

Works on Mac zsh too, and Linux bash 4.

Comments

1

This seems like it works to show the current shell PID and one of its child PIDs:

(echo $$; echo $(ps axo pid,ppid,command | awk -v p=$$ '$2 == p { print $1; exit }')) 14609 17365 

Note: This uses ps and awk to print the first child PID of the current shell ($$). It's a workaround for macOS, which doesn't come with pgrep by default.

On Linux, the cleaner and more accurate approach is:

(echo $$; echo $(pgrep -P $$)) 

Which might output:

14609 17390 

Caveat

As pointed out by anubhava, excessive use of pipes and subshells ((), backticks, $()) can spawn multiple layers of processes. This means $$ might not refer to the shell you expect, especially inside () or when nested. So depending on your use case, results may vary slightly due to process hierarchy.

2 Comments

Not really, this will print pid of sub+sub+sub+sub shell because of 3 pipes inside ( and ).
Anubhava can you look at this problem. It should be easy for you. This is the link: stackoverflow.com/questions/31961615/…

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.