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.

asked Feb 2, 2012 at 20:56
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.

answered Feb 2, 2012 at 23:43
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.

answered Feb 2, 2012 at 22:41

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)

answered Jul 20, 2012 at 3:20

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.

Glorfindel
22.8k13 gold badges96 silver badges124 bronze badges
answered Jul 19, 2012 at 23:42

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_)

answered Dec 11, 2015 at 23:13

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.

answered Jan 10 at 17:57

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.

answered Feb 2, 2012 at 23:19

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/…

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.