195

I have problem with Bash, and I don't know why.
Under shell, I enter:

echo $$    ## print 2433
(echo $$)  ## also print 2433
(./getpid) ## print 2602

Where getpid is a C program to get current pid, like:

   int main() {
    printf("%d", (int)getpid());
    return 0;
   }

What confuses me is that:

  1. I think "(command)" is a sub-process (am i right?), and i think its pid should be different with its parent pid, but they are the same, why...
  2. When I use my program to show pid between parenthesis, the pid it shows is different, is it right?
  3. Is $$ something like macro?

Can you help me?

2
  • 13
    Note that getpid would show a different process ID even if it weren't run in a subshell. Commented Jan 11, 2014 at 15:06
  • 3
    @Marian echo $$ $BASHPID ; ( echo $$ $BASHPID ) demonstrates that it does. Round brackets create a subshell. The statements may change variable values, and the parent shell must not see those changes. This is implemented as a fork() operation. Commented Jan 18, 2020 at 10:45

8 Answers 8

264

$$ is defined to return the process ID of the parent in a subshell; from the man page under "Special Parameters":

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

In bash 4, you can get the process ID of the child with BASHPID.

~ $ echo $$
17601
~ $ ( echo $$; echo $BASHPID )
17601
17634
Sign up to request clarification or add additional context in comments.

6 Comments

"parent" is a bit misleading (at least it was to me), it's actually the "top level" shell. For instance : echo $$; (echo $$; (echo $$)) echoes the same pid three times
Right; I should have said that the value is inherited from a parent shell (which inherited its value from its parent, etc). The top level shell sets it initially, rather than inheriting from its (non-shell) parent process.
$ Expands to the process ID of the shell does it tho? echo $ just echoes the literal $.
Ok I honestly have no idea what that means, but echo $BASHPID works in bash 4 and 5 (but not version 3.2.57 on MacOS)
All parameter expansions start with $, but $ is also the name of one of the special parameters. $, #, @, *, etc are some of the special parameters; $$, $#, $@, $*, etc are the expressions that expand to the value of each.
|
85

You can use one of the following.

  • $! is the PID of the last backgrounded process.
  • kill -0 $PID checks whether it's still running.
  • $$ is the PID of the current shell.

1 Comment

Shouldn't the second bullet be kill -0 $! if we're talking about background processes? PID isn't set to anything by default.
32
  1. Parentheses invoke a subshell in Bash. Since it's only a subshell it might have the same PID - depends on implementation.
  2. The C program you invoke is a separate process, which has its own unique PID - doesn't matter if it's in a subshell or not.
  3. $$ is an alias in Bash to the current script PID. See differences between $$ and $BASHPID here, and right above that the additional variable $BASH_SUBSHELL which contains the nesting level.

Comments

8

Try getppid() if you want your C program to print your shell's PID.

1 Comment

Or if you use Rust just try nix::unistd::getppid() which will get the pid of current processes' parent - docs.rs/nix/0.18.0/nix/unistd/fn.getppid.html
3

this one univesal way to get correct pid

pid=$(cut -d' ' -f4 < /proc/self/stat)

same nice worked for sub

SUB(){
    pid=$(cut -d' ' -f4 < /proc/self/stat)
    echo "$$ != $pid"
}

echo "pid = $$"

(SUB)

check output

pid = 8099
8099 != 8100

2 Comments

Nice idea, but won't that get you the pid of the fork the shell has run to capture the output of cut? If I run it twice, once with echo $(...) and once without, then I get different answers.
This only gets you the pid of the forked shell, as Martin points out. If you want to capture the current shell, invoking the forked command, you need the 5th field. -f5.
1

If you were asking how to get the PID of a known command it would resemble something like this:

If you had issued the command below #The command issued was ***

dd if=/dev/diskx of=/dev/disky


Then you would use:

PIDs=$(ps | grep dd | grep if | cut -b 1-5)

What happens here is it pipes all needed unique characters to a field and that field can be echoed using

echo $PIDs

Comments

0

if you want a simple shell script for getting the maximum PID with variable, do this

pid=$(cat /proc/sys/kernel/pid_max)
echo $pid

that will print you the maximum PID can be.

1 Comment

That is not the least bit relevant to this question
0

Portable way of achieving that

get_own_pid() {
  # This function being called in a subshell,
  # it must returns the pid of the parent of the "cut" command parent
  cut -d' ' -f4 < /proc/self/stat \
    | xargs -I% sh -c 'cut -d" " -f4 < /proc/%/stat'
}

get_parent_pid() {
  # Same thing but repeating the last command once more to get the parent one level above
  cut -d' ' -f4 < /proc/self/stat \
    | xargs -I% sh -c 'cut -d" " -f4 < /proc/%/stat' \
    | xargs -I% sh -c 'cut -d" " -f4 < /proc/%/stat'
}


# Here pid is the same as the $$ pid because called from main process
MY_PID=$(get_own_pid)
echo "$$ == ${MY_PID}"

# Here called in a subprocess, the returned pid is different
(
  MY_CHILD_PID=$(get_own_pid)
  PARENT_PID_FROM_CHILD=$(get_parent_pid)
  echo "$$ != ${MY_CHILD_PID}"
  echo "$$ == ${PARENT_PID_FROM_CHILD}"
)

Inspired from artem lapkin answer, thanks!

Comments

Your Answer

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.