Cómo obtener pid del proceso recién iniciado

71

Quiero iniciar el proceso (por ejemplo, myCommand) y obtener su pid (para permitir matarlo más tarde).

Intenté ps y filtro por nombre, pero no puedo distinguir el proceso por nombres

myCommand
ps ux | awk '/<myCommand>/ {print $2}' 

Porque los nombres de los procesos no son únicos.

Puedo ejecutar el proceso por:

myCommand &

Descubrí que puedo obtener este PID:

echo $!

¿Hay alguna solución más simple?

Me encantaría ejecutar myCommand y obtener su PID como resultado de un comando de línea.

rafalmag
fuente

Respuestas:

77

¿Qué puede ser más simple que echo $!? Como una línea:

myCommand & echo $!
Jacek Konieczny
fuente
Gracias fusionar estos comandos con "&" me ayudó mucho.
rafalmag
8
en un script bash, en un bucle que inicia programas, $! No es exacto. A veces devuelve pid del script en sí, a veces de grep o awk ejecutado desde el script ¿alguna solución para obtener específicamente el pid del proceso que se acaba de iniciar en este escenario? algo así como pid = myprogramhabría sido impresionante
2
NB que esto requiere que inicie el comando utilizando a &como la línea anterior, de lo contrario, el eco básicamente vuelve en blanco.
rogerdpack
2
asignar a variable como command & echo $!congela la ejecución en este paso :(
Shashank Vivek
26

Envuelva el comando en un pequeño script

#!/bin/bash
yourcommand &
echo $! >/path/to/pid.file
user9517 es compatible con GoFundMonica
fuente
20

Puede usar sh -cy execpara obtener el PID del comando incluso antes de que se ejecute.

Para comenzar myCommand, para que su PID se imprima antes de que comience a ejecutarse, puede usar:

sh -c 'echo $$; exec myCommand'

Cómo funciona:

Esto inicia un nuevo shell, imprime el PID de ese shell y luego usa el execincorporado para reemplazar el shell con su comando, asegurando que tenga el mismo PID. Cuando su shell ejecuta un comando con el execbuiltin incorporado, en realidad se está convirtiendo en ese comando , en lugar del comportamiento más común de bifurcar una nueva copia de sí mismo, que tiene su propio PID separado y que luego se convierte en el comando.

Considero que esto es mucho más simple que las alternativas que involucran ejecución asincrónica (con &), control de trabajo o búsqueda con ps. Esos enfoques están bien, pero a menos que tenga una razón específica para usarlos, por ejemplo, tal vez el comando ya se esté ejecutando, en cuyo caso buscar su PID o usar el control de trabajo tendría sentido, sugiero que considere esto primero. (Y ciertamente no consideraría escribir un script complejo u otro programa para lograr esto).

Esta respuesta incluye un ejemplo de esta técnica.


Algunas partes de ese comando podrían omitirse ocasionalmente, pero generalmente no.

Incluso si el shell que está utilizando es de estilo Bourne y, por lo tanto, es compatible execcon esta semántica, generalmente no debe intentar evitar el uso sh -c(o equivalente) para crear un nuevo proceso de shell separado para este propósito, porque:

  • Una vez que el shell se ha convertido myCommand, no hay ningún shell esperando para ejecutar comandos posteriores. sh -c 'echo $$; exec myCommand; foono podría intentar ejecutarse foodespués de reemplazarse por sí mismo myCommand. A menos que esté escribiendo un script que ejecute esto como su último comando, no puede simplemente usarlo echo $$; exec myCommanden un shell donde está ejecutando otros comandos.
  • No puede usar una subshell para esto. (echo $$; exec myCommand)puede ser sintácticamente mejor que sh -c 'echo $$; exec myCommand', pero cuando se ejecuta en el $$interior ( ), proporciona el PID del shell principal, no del subshell en sí. Pero es el PID del subshell el que será el PID del nuevo comando. Algunos shells proporcionan sus propios mecanismos no portátiles para encontrar el PID del subshell, que puede usar para esto. En particular, en Bash 4 , (echo $BASHPID; exec myCommand)funciona.

Finalmente, tenga en cuenta que algunos shells realizarán una optimización donde ejecutan un comando como si exec(es decir, renunciaran a bifurcar primero) cuando se sabe que el shell no necesitará hacer nada después. Algunos shells intentan hacer esto cada vez que es el último comando que se ejecuta, mientras que otros solo lo hacen cuando no hay otros comandos antes o después del comando, y otros no lo hacen en absoluto. El efecto es que si olvida escribir execy simplemente usa sh -c 'echo $$; myCommand', a veces le dará el PID correcto en algunos sistemas con algunos shells. Recomiendo no depender nunca de ese comportamiento y, en su lugar, siempre incluir execcuándo es lo que necesita.

Eliah Kagan
fuente
Antes de poder ejecutar myCommand, necesito establecer una serie de variables de entorno en mi script bash. ¿Se trasladarán al entorno en el que execse ejecuta el comando?
user5359531
Parece que mi entorno se transfiere al execcomando. Sin embargo, este enfoque no funciona cuando se myCommandinician otros procesos, con los que debe trabajar; cuando emito un kill -INT <pid>dónde pidse obtuvo de esta manera, la señal no alcanza los subprocesos iniciados por myCommand, mientras que si ejecuto myCommand en la sesión actual y Ctrl + C, las señales se propagan correctamente.
user5359531
1
Intenté esto, pero el pid del proceso myCommand parece ser la salida pid de echo $$ +1. ¿Estoy haciendo algo mal?
crobar
Mi comando se ve así:sh -c 'echo $$; exec /usr/local/bin/mbdyn -f "input.file" -o "/path/to/outputdir" > "command_output.txt" 2>&1 &'
crobar
7

No conozco ninguna solución más simple, ¡pero no estoy usando $! ¿suficientemente bueno? Siempre puede asignar el valor a alguna otra variable si lo necesita más tarde, como lo han dicho otros.

Como nota al margen, en lugar de canalizar desde ps podría usar pgrepo pidof.

carlpett
fuente
5

use exec de un script bash después de registrar el pid en un archivo:

ejemplo:

supongamos que tiene un script llamado "forever.sh" que desea ejecutar con los argumentos p1, p2, p3

código fuente forever.sh:

#!/bin/sh

while [ 1 -lt 2 ] ; do
    logger "$0 running with parameters \"$@\""
    sleep 5
done

crear un reaper.sh:

#!/bin/sh

echo $$ > /var/run/$1.pid
exec "$@"

ejecutar forever.sh a través de reaper.sh:

./reaper.sh ./forever.sh p1 p2 p3 p4 &

forever.sh no hace más que registrar una línea en syslog cada 5 segundos

ahora tienes el pid en /var/run/forever.sh.pid

cat /var/run/forever.sh.pid 
5780

y forever.sh se está ejecutando aok. syslog grep:

Nov 24 16:07:17 pinkpony cia: ./forever.sh running with parameters "p1 p2 p3 p4"

puedes verlo en la tabla de procesos:

ps axuwww|grep 'forever.sh p1' |grep -v grep
root      5780  0.0  0.0   4148   624 pts/7    S    16:07   0:00 /bin/sh ./forever.sh p1 p2 p3 p4
usuario237419
fuente
3
oh, y el "oneliner": / bin / sh -c 'echo $$> / tmp / my.pid && exec program args' &
user237419
1
Para conservar adecuadamente los espacios en blanco internos en los argumentos, debe usarlos en exec "$@"lugar de exec $*. Técnicamente, lo que necesita preservar no es el espacio en blanco, sino las apariciones de los caracteres en el parámetro de shell IFS (que por defecto es espacio, tabulación y nueva línea).
Chris Johnsen
punto a favor. :)
user237419
Gracias, no sabía el parámetro $$. Puede ser muy útil
rafalmag
3

En el shell bash, una alternativa $!podría ser el jobs -pincorporado. En algunos casos el !en $!se interpreta por la cáscara antes de (o en lugar de) la expansión de variables, lo que lleva a resultados inesperados.

Esto, por ejemplo, no funcionará:

((yourcommand) & echo $! >/var/run/pidfile)

mientras que esto:

((yourcommand) & jobs -p >/var/run/pidfile)
mustaccio
fuente
Creo que te referías a ((yourcommand) & jobs -p> / var / run / pidfile).
Dom
1

Puedes usar algo como:

$ myCommand ; pid=$!

O

$ myCommand && pid=$!

Los dos comandos pueden ser uniones usando ;o &&. En el segundo caso, el pid se establecerá solo si el primer comando tiene éxito. Puede obtener la identificación del proceso $pid.

Khaled
fuente
3
OP quiere obtener el PID para poder matarlo más tarde. ; y && requieren que el proceso original salga antes del echo $! es ejecutado.
user9517 es compatible con GoFundMonica el
Sí, tiene usted razón. Esto le dará el pid después de que myCommand haya terminado.
Khaled
66
Hacer referencia $!después &&o ;nunca le dará el PID del proceso iniciado para el lado izquierdo del separador de comandos. $!solo está configurado para procesos iniciados de forma asincrónica (por ejemplo, generalmente con &algunos shells pero también tienen otros métodos)
Chris Johnsen
es útil, y ¿por qué nadie vota excepto yo? aunque buen trabajo :)
templo
1

Esta es una respuesta un poco hack-y y probablemente no funcionará para la mayoría de las personas. También es un gran riesgo de seguridad, así que no lo hagas a menos que estés seguro de que estarás seguro y las entradas están desinfectadas y ... bueno, ya tienes la idea.

Compile el pequeño programa C aquí en un binario llamado start(o lo que quiera), luego ejecute su programa como./start your-program-here arg0 arg1 arg2 ...

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    if (argc >= 2)
    {
        printf("%lu\n", (long unsigned) getpid());
        if (execvp(argv[1], &argv[1]) < 0)
        {
            perror(NULL);
            return 127;
        }
    }
    return 0;
}

En pocas palabras, esto imprimirá el PID y stdoutluego cargará su programa en el proceso. Aún debe tener el mismo PID.

tonysdg
fuente
¿Cómo puedo registrar el número PID devuelto por este código en una variable bash? Por una razón que no me stdoutqueda clara, no parece estar registrada en este ejemplo:RESULT="$(./start_and_get_pid.out echo yo)"; echo "$RESULT"
Tfb9
@ Tfb9: Sinceramente, recomendaría uno de los otros enfoques; Escribí esto hace más de 2 años y es, con mucho, uno de los métodos más propensos a hackear / propensos a errores presentados (o eso creo).
tonysdg
Gracias por la nota Creo que resolví mi problema con estosleep 10 & PID_IS=$!; echo $PID_IS
Tfb9