Cómo obtener el PID de subshell en Korn Shell (equivalente a $ BASHPID)

8

En bash, tiene esta variable útil: $ BASHPID que siempre devuelve el PID del subshell actualmente en ejecución. ¿Cómo puedo obtener el PID de una subshell en ksh? Por ejemplo, vea el código a continuación:

#!/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 $!"

Esto genera lo siguiente:

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

Lo que quiero es que la línea comience con la ****salida del PID del subshell, en el caso del ejemplo que sería 5329.

Patkos Csaba
fuente

Respuestas:

10

No creo que esté disponible en ksh. Hay una solución POSIX que implica ejecutar un proceso externo:

sh -c 'echo $PPID'

En Linux, readlink /proc/selftambién funcionaría, pero no veo ninguna ventaja (podría ser marginalmente más rápido; podría ser útil en una variante de BusyBox que tiene readlinkpero no $PPID, pero no creo que haya una).

Tenga en cuenta que para obtener el valor en el shell, debe tener cuidado de no ejecutar ese comando en un sub-sub-shell de corta duración. Por ejemplo, p=$(sh -c 'echo $PPID')podría mostrar la salida del subshell que se invoca shdentro de la sustitución del comando (o puede que no, algunos shells optimizan ese caso). En cambio, corre

p=$(exec sh -c 'echo $PPID')
Gilles 'SO- deja de ser malvado'
fuente
Ya vi esta sugerencia pero no funciona. Me da un tercer PID ... sin embargo, lo probaré nuevamente el lunes cuando vuelva a trabajar.
Patkos Csaba
@PatkosCsaba En ksh probablemente funcionaría porque ksh optimiza las horquillas, pero en algunos otros shells como bash debes tener cuidado de no obtener accidentalmente el pid de un sub-sub-shell. Ver mi respuesta actualizada.
Gilles 'SO- deja de ser malvado'
Esto está funcionando: sin $(exec sh -c 'echo $PPID')embargo, el comando simple inicial sh -c 'echo $PPID'proporciona un tercer PID. Así que gracias por la solución. Aceptado.
Patkos Csaba
1
@FranklinYu Eso se debe a que ninguno de los dos está creando una subshell. Bash se optimiza (sh -c 'echo $PPID')para evitar crear una subshell. Contraste con (sh -c 'echo $PPID'; true). Esta optimización solo se activa si intenta acceder $BASHPIDcomo lo último antes de que salga la subshell, es decir, solo en los casos en que no pueda hacer nada con el valor. Entonces, en la práctica, puede reemplazar $BASHPIDcon $(sh -c 'echo $PPID').
Gilles 'SO- deja de ser malvado'
1
@FranklinYu No creo que haya nada incorporado. Por supuesto, puedes usar el método portátil sh -c 'echo $PPID'.
Gilles 'SO- deja de ser malvado'
3

Puede lograr lo que desea, pero necesita poner run_something en un script separado. No estoy exactamente seguro de por qué, pero $$ no se vuelve a evaluar cuando se usa en una función en el mismo script que lo está llamando. Supongo que el valor de $$ se asigna una vez después de analizar el script y antes de ejecutarlo.

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;

salida

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
Justin Rowe
fuente
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
fuente
Dos nitpicks: if [[ ${!KSH_VERSION} = .sh.version ]]; then(solo uno =) y elif [[ -z ${BASHPID+_} ]]; then(evite usar el implícito -nentre corchetes dobles, el viejo pdksh no lo sabía).
mirabilos
0

Siguiendo la respuesta de @Gilles que encontré al resolver otro problema que tenía, preparé un programa de prueba rápida que sustenta la teoría de que la respuesta correcta es:

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

Descubrí que hay momentos en execque no es necesario, pero confirmó que usarlo es la única forma de obtener el pid correcto todo el tiempo en todos los shells que probé. Aquí está mi prueba:

#!/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)"

y su salida

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

Sustituir su shell favorito en el tinglado: sh, bash, mksh, ksh, etc ...

No entiendo por qué los casos 2 y 3 dan resultados diferentes, ni por qué los resultados para el caso 3 difieren entre los shells. Probé bash, kshy mkshen Arch Linux Fwiw.

starfry
fuente
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
Alty
fuente