8

In bash you have this handy variable: $BASHPID wich always returns the currently running subshell's PID. How can I get a subshell's PID in ksh? For example see the code below:

#!/usr/bin/ksh93

echo "PID at start: $$"

function run_in_background {
  echo "PID in run_in_background $$"
  run_something &
  echo "PID of backgrounded run_something: $!"
}

function run_something {
  echo "*** PID in run_something: $$"
  sleep 10;
}    

run_in_background
echo "PID of run in background $!"

This outputs the following:

PID at start: 5328
PID in run_in_background 5328
*** PID in run_something: 5328
PID of backgrounded run_something: 5329
PID of run in background 5329

What I want is the line starting with **** to output the subshell's PID, in the example's case that would be 5329.

Patkos Csaba
  • 2,530

5 Answers5

11

I don't think that's available in ksh. There's a POSIX solution which involves running an external process:

sh -c 'echo $PPID'

On Linux, readlink /proc/self would also work, but I fail to see any advantage (it might be marginally faster; it could be useful on a BusyBox variant that has readlink but not $PPID, but I don't think there is one).

Note that in order to get the value in the shell, you need to be careful not to run that command in a short-lived sub-sub-shell. For example, p=$(sh -c 'echo $PPID') might show the output of the subshell that invokes sh within the command substitution (or it might not, some shells optimize that case). Instead, run

p=$(exec sh -c 'echo $PPID')
  • I saw this suggestion already but it is not working. It gives me a third PID ... however I will test it again Monday when I'm back at work. – Patkos Csaba Feb 25 '12 at 15:45
  • @PatkosCsaba In ksh it would probably work because ksh optimizes forks, but in some other shells such as bash you need to be careful not to accidentally get the pid of a sub-sub-shell. See my updated answer. – Gilles 'SO- stop being evil' Feb 25 '12 at 20:08
  • This is working: $(exec sh -c 'echo $PPID') However the initial simple command sh -c 'echo $PPID' gives a third PID. So thanks for the solution. Accepted. – Patkos Csaba Feb 27 '12 at 08:29
  • How can $BASHPID be emulated with sh -c 'echo $PPID'? I tried (sh -c 'echo $PPID') and (exec sh -c 'echo $PPID') in Bash but both give me the same PID as $$. – Franklin Yu Aug 22 '18 at 17:26
  • 1
    @FranklinYu That's because neither is creating a subshell. Bash optimizes (sh -c 'echo $PPID') to avoid creating a subshell. Contrast with (sh -c 'echo $PPID'; true). This optimization only kicks in if you try to access $BASHPID as the very last thing before the subshell exits, i.e. only in cases where you can't do anything with the value. So in practice, you can replace $BASHPID with $(sh -c 'echo $PPID'). – Gilles 'SO- stop being evil' Aug 22 '18 at 17:32
  • @Gilles Thanks a lot. Is there anything like $BASHPID in Zsh? – Franklin Yu Aug 22 '18 at 18:19
  • 1
    @FranklinYu I don't think there's anything built in. You can of course use the portable method sh -c 'echo $PPID'. – Gilles 'SO- stop being evil' Aug 22 '18 at 18:41
3

You can achieve what you want, but you need to put run_something into a separate script. I'm not exactly sure why, but $$ is not re-evaluated when it is used in a function in the same script that is calling it. I guess that the value of $$ is assigned once after the script is parsed and before it is executed.

run_in_background.sh

#
echo "PID at start: $$"

    function run_in_background {
      echo "PID in run_in_background $$"
      ./run_something.sh &
      echo "PID of backgrounded run_something: $!"
    }

    run_in_background
    echo "PID of run in background $!"

run_something.sh

#
echo "*** PID in run_something: $$"
sleep 10;

output

PID at start: 24395
PID in run_in_background 24395
PID of backgrounded run_something: 24396
PID of run in background 24396
*** PID in run_something: 24396
1
# KSH_VERSION hasn't always been a nameref, nor has it always existed.
# Replace with a better test if needed. e.g.:
# https://www.mirbsd.org/cvs.cgi/contrib/code/Snippets/getshver?rev=HEAD
if [[ ${!KSH_VERSION} == .sh.version ]]; then
    # if AT&T ksh
    if builtin pids 2>/dev/null; then # >= ksh93 v- alpha
        function BASHPID.get { .sh.value=$(pids -f '%(pid)d'); }
    elif [[ -r /proc/self/stat ]]; then # Linux / some BSDs / maybe others
        function BASHPID.get { read -r .sh.value _ </proc/self/stat; }
    else # Crappy fallback
        function BASHPID.get { .sh.value=$(exec sh -c 'echo $PPID'); }
    fi
elif [[ ! ${BASHPID+_} ]]; then
   echo 'BASHPID requires Bash, ksh93, or mksh >= R41' >&2
   exit 1
fi
ormaaj
  • 752
  • Two nitpicks: if [[ ${!KSH_VERSION} = .sh.version ]]; then (only one =) and elif [[ -z ${BASHPID+_} ]]; then (avoid using the implicit -n in double-square-brackets, old pdksh didn’t know it). – mirabilos Feb 27 '14 at 14:06
0

Following the answer by @Gilles which I encountered whilst solving another problem that I had, I threw together a quick test program that underpins the theory that the correct answer is:

MYPID=$(exec sh -c 'echo $PPID')

I found there are times when exec isn't required but I confirmed that using it is the only way to get the correct pid all of the time in all of the shells I tried. Here is my test:

#!/bin/sh
pids() {
  echo ------
  pstree -pg $PPID
  echo 'PPID = ' $PPID
  echo '$$ = ' $$
  echo 'BASHPID =' $BASHPID
  echo -n 'sh -c echo $PPID = '; sh -c 'echo $PPID'
  echo -n '$(sh -c '\''echo $PPID'\'') = '; echo $(sh -c 'echo $PPID') 
  echo -n '$(exec sh -c '\''echo $PPID'\'') = '; echo $(exec sh -c 'echo $PPID') 
  echo -n 'p=$(sh -c '\''echo $PPID'\'') = '; p=$(sh -c 'echo $PPID') ; echo $p
  echo -n 'p=$(exec sh -c '\''echo $PPID'\'') = '; p=$(exec sh -c 'echo $PPID') ; echo $p
}
pids
pids | cat
echo -e "$(pids)"

and its output

------
bash(5975,5975)---pidtest(13474,13474)---pstree(13475,13474)
PPID =  5975
$$ =  13474
BASHPID = 13474
sh -c echo $PPID = 13474
$(sh -c 'echo $PPID') = 13474
$(exec sh -c 'echo $PPID') = 13474
p=$(sh -c 'echo $PPID') = 13474
p=$(exec sh -c 'echo $PPID') = 13474
------
bash(5975,5975)---pidtest(13474,13474)-+-cat(13482,13474)
                                       `-pidtest(13481,13474)---pstree(13483,13474)
PPID =  5975
$$ =  13474
BASHPID = 13481
sh -c echo $PPID = 13481
$(sh -c 'echo $PPID') = 13481
$(exec sh -c 'echo $PPID') = 13481
p=$(sh -c 'echo $PPID') = 13481
p=$(exec sh -c 'echo $PPID') = 13481
------
bash(5975,5975)---pidtest(13474,13474)---pidtest(13489,13474)---pstree(13490,13474)
PPID =  5975
$$ =  13474
BASHPID = 13489
sh -c echo $PPID = 13489
$(sh -c 'echo $PPID') = 13492
$(exec sh -c 'echo $PPID') = 13489
p=$(sh -c 'echo $PPID') = 13495
p=$(exec sh -c 'echo $PPID') = 13489

Substitute your favourite shell in the shebang: sh, bash, mksh, ksh, etc...

I don't understand why cases 2 and 3 give different results, nor why the results for case 3 differ between shells. I tried bash, ksh and mksh on Arch Linux FWIW.

starfry
  • 7,442
0
#!/bin/ksh
 function os_python_pid {
/usr/bin/python  -c 'import os,sys ; sys.stdout.write(str(os.getppid()))'
}
function os_perl_pid {
/usr/bin/perl -e 'print getppid'
}

echo "I am $$"
    echo "I am $(os_python_pid) :: $(os_perl_pid)"
function proce {
  sleep 3
  echo "$1 :: $(os_python_pid) :: $(os_perl_pid)"
}

for x in aa bb cc; do
  proce $x &
  echo "Started: $!"
done
Kevdog777
  • 3,224
Alty
  • 1