Come ottenere il pid del processo appena iniziato


71

Voglio iniziare il processo (es. MyCommand) e ottenere il suo pid (per consentire di ucciderlo in seguito).

Ho provato ps e filtro per nome, ma non riesco a distinguere processo per nomi

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

Perché i nomi dei processi non sono univoci.

Posso eseguire il processo da:

myCommand &

Ho scoperto che posso ottenere questo PID da:

echo $!

C'è qualche soluzione più semplice?

Sarei felice di eseguire myCommand e ottenere il suo PID come risultato di un comando di linea.

Risposte:


77

Cosa può essere più semplice di echo $!? Come una riga:

myCommand & echo $!

Grazie l'unione di questi comandi con "&" mi ha aiutato molto.
rafalmag,

8
in uno script bash, in un ciclo che avvia i programmi, $! non è preciso. A volte restituisce pid dello script stesso, a volte grep o awk vengono eseguiti dallo script qualsiasi soluzione per ottenere specificamente il pid del processo appena avviato in questo scenario? qualcosa come pid = myprogramsarebbe stato fantastico

2
Nota che questo richiede di avviare il comando usando a &come la riga precedente, altrimenti l'eco sostanzialmente ritorna vuoto.
rogerdpack,

2
l'assegnazione a una variabile come command & echo $!congela l'esecuzione in questo passaggio :(
Shashank Vivek


20

È possibile utilizzare sh -ce execper ottenere il PID del comando anche prima che venga eseguito.

Per iniziare myCommand, in modo che il suo PID venga stampato prima che inizi l'esecuzione, è possibile utilizzare:

sh -c 'echo $$; exec myCommand'

Come funziona:

Questo avvia una nuova shell, stampa il PID di quella shell e quindi utilizza l' execintegrato per sostituire la shell con il tuo comando, assicurandoti che abbia lo stesso PID. Quando la shell esegue un comando con il comando execincorporato, la shell in realtà sta diventando quel comando , piuttosto che il comportamento più comune di creare una nuova copia di se stesso, che ha il proprio PID separato e che quindi diventa il comando.

Trovo che questo sia molto più semplice rispetto alle alternative che coinvolgono l'esecuzione asincrona (con &), il controllo del lavoro o la ricerca con ps. Questi approcci vanno bene, ma a meno che tu non abbia un motivo specifico per usarli - per esempio, forse il comando è già in esecuzione, nel qual caso ha senso cercare il suo PID o usare il controllo del lavoro - suggerisco di considerare prima questo modo. (E certamente non prenderei in considerazione la possibilità di scrivere una sceneggiatura complessa o un altro programma per raggiungere questo obiettivo).

Questa risposta include un esempio di questa tecnica.


Alcune parti di quel comando potevano essere occasionalmente omesse, ma di solito non.

Anche se la shell che stai usando è in stile Bourne e quindi supporta il execbuiltin con queste semantiche, generalmente non dovresti cercare di evitare di usare sh -c(o equivalente) per creare un nuovo processo di shell separato per questo scopo, perché:

  • Una volta che la shell è diventata myCommand, non c'è nessuna shell in attesa di eseguire i comandi successivi. sh -c 'echo $$; exec myCommand; foonon sarebbe in grado di tentare di eseguire foodopo aver sostituito se stesso con myCommand. A meno che tu non stia scrivendo uno script che esegue questo come ultimo comando, non puoi semplicemente usarlo echo $$; exec myCommandin una shell in cui stai eseguendo altri comandi.
  • Non è possibile utilizzare una subshell per questo. (echo $$; exec myCommand)può essere sintatticamente più bello di sh -c 'echo $$; exec myCommand', ma quando si esegue $$all'interno ( ), fornisce il PID della shell genitore, non della sottoshell stessa. Ma è il PID della subshell che sarà il PID del nuovo comando. Alcune shell forniscono i propri meccanismi non portatili per trovare il PID della subshell, che è possibile utilizzare per questo. In particolare, in Bash 4 , (echo $BASHPID; exec myCommand)funziona.

Infine, nota che alcune shell eseguiranno un'ottimizzazione in cui eseguono un comando come se exec(per esempio, rinunciano prima a biforcarsi) quando è noto che la shell non dovrà fare nulla in seguito. Alcune shell provano a farlo ogni volta che è l'ultimo comando da eseguire, mentre altri lo faranno solo quando non ci sono altri comandi prima o dopo il comando, e altri non lo faranno affatto. L'effetto è che se ti dimentichi di scrivere exece utilizzarlo sh -c 'echo $$; myCommand', a volte ti darà il PID giusto su alcuni sistemi con alcune shell. Consiglio di non fare affidamento su tale comportamento e di includere sempre execquando è quello che ti serve.


Prima di poter eseguire myCommand, devo impostare una serie di variabili d'ambiente nel mio script bash. Questi verranno trasferiti nell'ambiente in cui execè in esecuzione il comando?
user5359531,

Sembra che il mio ambiente riporti il execcomando. Tuttavia, questo approccio non funziona quando myCommandavvia altri processi, che sono quelli con cui devi lavorare; quando emetto un punto in kill -INT <pid>cui è pidstato ottenuto in questo modo, il segnale non raggiunge i sottoprocessi avviati da myCommand, mentre se corro myCommand nella sessione corrente e Ctrl + C, i segnali si propagano correttamente.
user5359531,

1
Ho provato questo, ma il pid del processo myCommand sembra essere l'output pid di echo $$ +1. Sto facendo qualcosa di sbagliato?
crobar,

Il mio comando è simile al seguente:sh -c 'echo $$; exec /usr/local/bin/mbdyn -f "input.file" -o "/path/to/outputdir" > "command_output.txt" 2>&1 &'
crobar,

7

Non conosco alcuna soluzione più semplice, ma non sto usando $! abbastanza buono? Puoi sempre assegnare il valore ad un'altra variabile se ne hai bisogno in seguito, come detto da altri.

Come nota a margine, invece di piping da ps potresti usare pgrepo pidof.


5

usa exec da uno script bash dopo aver registrato il pid in un file:

esempio:

supponiamo di avere uno script chiamato "forever.sh" che vuoi eseguire con args p1, p2, p3

forever.sh codice sorgente:

#!/bin/sh

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

crea un reaper.sh:

#!/bin/sh

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

corri per sempre.sh attraverso reaper.sh:

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

forever.sh non fa altro che registrare una riga su syslog ogni 5 secondi

ora hai il pid in /var/run/forever.sh.pid

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

e forever.sh è in esecuzione. syslog grep:

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

puoi vederlo nella tabella dei processi:

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

3
oh, e "oneliner": / bin / sh -c 'echo $$> / tmp / my.pid && exec program args' &
user237419

1
Per conservare correttamente gli spazi bianchi interni negli argomenti, è necessario utilizzare exec "$@"invece di exec $*. Tecnicamente ciò che devi preservare non sono gli spazi bianchi ma le occorrenze dei caratteri nel parametro della shell IFS (che per impostazione predefinita è spazio, tabulazione e riga nuova).
Chris Johnsen,

punto preso. :)
user237419

Grazie, non conoscevo il parametro $$. Può essere molto utile
rafalmag,

3

Nella shell bash un'alternativa $!potrebbe essere quella jobs -pincorporata. In alcuni casi, !in $!viene interpretato dalla shell prima (o anziché) dell'espansione della variabile, portando a risultati imprevisti.

Questo, ad esempio, non funzionerà:

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

mentre questo:

((yourcommand) & jobs -p >/var/run/pidfile)

Penso che volevi dire ((il tuo comando) e lavori -p> / var / run / pidfile).
Dom

1

Puoi usare qualcosa come:

$ myCommand ; pid=$!

O

$ myCommand && pid=$!

I due comandi possono essere giunti usando ;o &&. Nel secondo caso, il pid verrà impostato solo se il primo comando ha esito positivo. È possibile ottenere l'ID processo da $pid.


3
OP vuole ottenere il PID in modo che possa ucciderlo in seguito. ; e && richiedono che il processo originale termini prima dell'eco $! viene eseguito.
user9517 supporta GoFundMonica il

Si hai ragione. Questo ti darà il pid dopo che myCommand è terminato.
Khaled,

6
Fare riferimento $!dopo &&o ;non ti darà mai il PID del processo avviato per il lato sinistro del separatore di comandi. $!è impostato solo per i processi avviati in modo asincrono (ad es. di solito con &ma alcune shell hanno anche altri metodi).
Chris Johnsen,

è utile e perché nessuno vota in alto mi esclude? buon lavoro però :)
tempio il

1

Questa è una risposta un po 'hack-y e probabilmente non funzionerà per la maggior parte delle persone. È anche un grosso rischio per la sicurezza, quindi non farlo a meno che tu non sia sicuro che sarai al sicuro e che gli input siano disinfettati e ... beh, hai capito.

Compila il piccolo programma C qui in un file binario chiamato start(o come vuoi), quindi esegui il tuo programma come./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;
}

Per farla breve, questo stampa il PID stdout, quindi carica il programma nel processo. Dovrebbe comunque avere lo stesso PID.


Come posso registrare il numero PID restituito da questo codice in una variabile bash? Per un motivo poco chiaro per me, stdoutnon sembra essere registrato in questo esempio:RESULT="$(./start_and_get_pid.out echo yo)"; echo "$RESULT"
Tfb9

@ Tfb9: consiglierei onestamente uno degli altri approcci; L'ho scritto più di 2 anni fa ed è di gran lunga uno dei metodi per hacker / soggetti a errori presentati (o almeno credo).
tonysdg

Grazie per la nota. Penso di aver risolto il mio problema con questosleep 10 & PID_IS=$!; echo $PID_IS
Tfb9
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.