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.

Community
  • 1
  • 1
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • Possible duplicate: http://stackoverflow.com/questions/5615570/in-a-script-vs-in-a-subshell – pilcrow Feb 02 '12 at 21:44
  • @pilcrow: It is no way duplicate since `echo $BASHPID` doesn't work on my Mac and I mentioned it clearly in my question that `[Solution should work on Mac as well not just Linux]` – anubhava Feb 02 '12 at 22:02
  • 4
    I believe BASHPID requires Bash 4, and OS X ships with Bash 3.2 – Lily Ballard Feb 02 '12 at 23:28
  • 1
    @anubhava: True, but we (the SO community) didn't know that until you edited ;-) – David Z Feb 02 '12 at 23:29
  • @DavidZaslavsky: Not really, I always had this line `[Solution should work on Mac as well not just Linux]` in my question and also included tag osx in my question. – anubhava Feb 02 '12 at 23:35
  • 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. – David Z Feb 02 '12 at 23:37
  • @DavidZaslavsky: Agreed. Just fyi I just provided answer to my question here. – anubhava Feb 02 '12 at 23:45
  • 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 http://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. – frankc Feb 03 '12 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: http://serverfault.com/questions/205498/how-to-get-pid-of-just-started-process – rogerdpack Feb 04 '15 at 18:57

6 Answers6

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.

anubhava
  • 761,203
  • 64
  • 569
  • 643
  • 3
    `sh -c 'echo $PPID`' is a shorter way to do the same – evil otto Feb 03 '12 at 00:25
  • 3
    One oddity: when I run `echo $$; ( bash -c 'echo $PPID'; )` on my computer (Linux), it prints the same pid twice... – Chris Dodd Feb 03 '12 at 22:10
  • @ChrisDodd: Very valid observation, I reverted back to my original solution. – anubhava Feb 03 '12 at 22:38
  • 6
    @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. – ecatmur Oct 28 '13 at 11:34
  • 1
    @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 – Maxim Kholyavkin Feb 27 '14 at 13:16
  • 1
    Personally this worked for me ```echo $$; (echo 'echo $PPID' | bash)``` just another variation of the above. – Michael C. Chen Feb 03 '20 at 09:51
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.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
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)

Zulu
  • 8,765
  • 9
  • 49
  • 56
2

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

Check out Link to install Homebrew.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
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_)

1

This seems like it works:

(echo $$; echo  `ps axo pid,command,args | grep "$$" |awk '{ getline;print $1}'`)
14609
17365

For whatever reason, OSX is limited and doesnt come with pgrep, or one could do (which works in Linux):

 (echo $$; echo  `pgrep -P $$`) 
 14609
 17390
masseo
  • 49
  • 3
  • 1
    Not really, this will print pid of sub+sub+sub+sub shell because of 3 pipes inside ( and ). – anubhava Feb 02 '12 at 23:26
  • Anubhava can you look at this problem. It should be easy for you. This is the link: http://stackoverflow.com/questions/31961615/what-to-change-in-htaccess-so-it-stop-showing-subdomain – user3082821 Aug 12 '15 at 11:09