¿Cómo puedo obtener el pid de una subshell?

13

¿Cómo puedo obtener el pid de una subshell?

Por ejemplo:

$ echo $$
16808

Esto no funciona porque el shell original se expande $$:

$ ( echo $$ )
16808

¿Por qué no funciona la comilla simple? Después de que el shell original elimina la comilla simple, ¿el subshell no se expande $$en sí mismo?

$ ( echo '$$' )
$$

¿Por qué tampoco evalfunciona? ¿Es evalejecutado por la subshell? ¿Por qué me da el PID del shell original?

$ ( eval echo '$$' )
16808

Gracias.

Tim
fuente
Sugiero una reapertura, porque las preguntas son esencialmente diferentes en mi opinión ("cómo evitar la $$expansión" frente a "pid diferente en el subshell").
peterh - Restablece a Monica el

Respuestas:

12

Además de bash's $BASHPID, puede hacerlo de forma portátil con:

pid=$(exec sh -c 'echo "$PPID"')

Ejemplo:

(pid=$(exec sh -c 'echo "$PPID"'); echo "$$ $pid")

Puedes convertirlo en una función:

# usage getpid [varname]
getpid(){
    pid=$(exec sh -c 'echo "$PPID"')
    test "$1" && eval "$1=\$pid"
}

Observe que algunos shells (p. Ej. zshO ksh93) NO inician un subproceso para cada subshell creado con (...); en ese caso, $pidpuede terminar siendo el mismo $$, lo cual es correcto, porque ese es el PID del proceso getpiddesde el que se llamó.

Mosvy
fuente
1
No. Pero no asuma que una subshell se ejecuta necesariamente en un subproceso; ese no es el caso ksh93, por ejemplo.
mosvy
1
Funcionará bien en ksh93: siempre devolverá el pid del proceso desde el que se llamó. Es del (...)ejemplo que puede no generar un proceso separado, como lo hace en bash.
mosvy
1
Además, a algunos shells les gusta zshu yashoptimizan a fork()para el último comando en un subshell. Incluso pueden optimizar la bifurcación para la subshell si es el último comando en un script para getpidque incluso pueda informar al padre $$. Puede definir getpidcomo: getpid(){ sh -c 'echo "$PPID"'; return; }para deshabilitar evitar el problema.
Stéphane Chazelas
1
@HaroldFischer 1. sin execo sin esa optimización, el sh -c ...proceso será un nieto, en lugar de un hijo del proceso donde $(...)se usa una sustitución de comando, y $PPIDserá el pid de la $(...)subshell. Eso es exactamente lo que sucede en el ejemplo set -E+ trap ERRbash anterior.
mosvy
1
@HaroldFischer 2. test "$1"prueba si $1es una cadena vacía o no, una forma rápida y sucia de probar si esa función recibió un varnameargumento para asignar o no el pid; El uso de una función no era la idea más brillante en primer lugar.
mosvy
18
$ echo $BASHPID
37152
$ ( echo $BASHPID )
18633

Del manual:

BASHPID

Se expande al ID de proceso del proceso bash actual. Esto difiere de $$bajo ciertas circunstancias, como subcapas que no requieren reiniciar bash.

$

Se expande al ID de proceso del shell. En un ()subshell, se expande al ID de proceso del shell actual, no al subshell.

Relacionado:

Kusalananda
fuente
Gracias. (1) ¿Qué significa "reinicializar"? (2) ¿Podría también considerar por qué esas formas que he intentado no funcionan?
Tim
@Tim Creo que esto es respondido por Gilles aquí . Bash simplemente no se actualiza $$en subcapas.
Kusalananda
¿Quiere decir que siempre debería usar $ BASHPID en lugar de $$ en cualquier caso en bash? ¿Cuándo debo usar cuál?
Tim
@Tim Depende de si usted, en una subshell, desea obtener el ID del proceso del script o de la subshell. Se proporcionan ambas posibilidades y cuál es la correcta depende de la aplicación. No se puede dar una respuesta más específica a eso.
Kusalananda
1
@Tim El PID de un shell primario de un subshell no se puede encontrar de manera confiable a menos que organice guardar $BASHPIDen una variable y usarlo en el subshell. Existe $PPID, pero ese es el PID primario del shell en el mismo sentido que $$el PID del shell (no se restablece en un subshell). No hay $BASHPPIDvariable
Kusalananda