Wie bekomme ich pid von gerade begonnenen Prozess

71

Ich möchte den Prozess starten (zB myCommand) und seine PID erhalten (um ihn später töten zu können).

Ich habe ps ausprobiert und nach Namen gefiltert, aber ich kann den Prozess nicht nach Namen unterscheiden

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

Weil Prozessnamen nicht eindeutig sind.

Ich kann den Prozess ausführen durch:

myCommand &

Ich habe festgestellt, dass ich diese PID erhalten kann durch:

echo $!

Gibt es eine einfachere Lösung?

Ich würde gerne myCommand ausführen und seine PID als Ergebnis eines einzeiligen Befehls erhalten.

rafalmag
quelle

Antworten:

77

Was kann einfacher sein als echo $!? Als eine Zeile:

myCommand & echo $!
Jacek Konieczny
quelle
Vielen Dank, dass Sie diese Befehle mit "&" zusammengeführt haben, was mir sehr geholfen hat.
Rafalmag
8
in einem Bash-Skript, in einer Schleife, die Programme startet, $! ist nicht genau. Manchmal gibt es die PID des Skripts selbst zurück, manchmal grep oder awk, die vom Skript ausgeführt werden. so etwas wie pid = myprogramwäre
2
Beachten Sie, dass Sie dazu den Befehl mit a &wie in der vorherigen Zeile starten müssen , andernfalls wird das Echo im Grunde leer zurückgegeben.
Rogerdpack
2
Durch Zuweisen zu Variable like wird command & echo $!die Ausführung in diesem Schritt eingefroren :(
Shashank Vivek,
26

Schließen Sie den Befehl in ein kleines Skript ein

#!/bin/bash
yourcommand &
echo $! >/path/to/pid.file
user9517
quelle
20

Sie können sh -cund verwenden exec, um die PID des Befehls zu ermitteln, noch bevor dieser ausgeführt wird.

Zum Starten myCommand, damit die PID gedruckt wird, bevor die Ausführung beginnt, können Sie Folgendes verwenden:

sh -c 'echo $$; exec myCommand'

Wie es funktioniert:

Dies startet eine neue Shell, gibt die PID dieser Shell aus und ersetzt die Shell mit dem execeingebauten Befehl, um sicherzustellen, dass sie dieselbe PID hat. Wenn Ihre Shell einen Befehl mit der eingebauten Funktion ausführt, wird Ihre Shell tatsächlich zu diesem Befehl und nicht zu dem häufigeren Verhalten, eine neue Kopie von sich selbst zu erstellen, die eine eigene separate PID hat und dann zum Befehl wird.exec

Ich finde das viel einfacher als Alternativen, die asynchrone Ausführung (mit &), Jobsteuerung oder Suche mit beinhalten ps. Diese Ansätze sind in Ordnung, aber es sei denn, Sie haben einen bestimmten Grund, sie zu verwenden. Beispielsweise wird der Befehl bereits ausgeführt. In diesem Fall ist es sinnvoll, nach seiner PID zu suchen oder die Jobsteuerung zu verwenden. (Und ich würde sicherlich nicht in Betracht ziehen, ein komplexes Skript oder ein anderes Programm zu schreiben, um dies zu erreichen).

Diese Antwort enthält ein Beispiel für diese Technik.


Teile dieses Befehls könnten gelegentlich weggelassen werden, aber normalerweise nicht.

Auch wenn die von Ihnen verwendete Shell einen Bourne-Stil hat und daher die execintegrierte Semantik unterstützt, sollten Sie im Allgemeinen nicht versuchen, die Verwendung sh -c(oder eine gleichwertige Methode ) zum Erstellen eines neuen, separaten Shell-Prozesses für diesen Zweck zu vermeiden , da:

  • Sobald die Shell geworden ist myCommand, gibt es keine Shell, die darauf wartet, nachfolgende Befehle auszuführen. sh -c 'echo $$; exec myCommand; foowäre nicht in der Lage zu versuchen, foonach dem Ersetzen durch myCommand. Wenn Sie kein Skript schreiben, das dieses als letzten Befehl ausführt, können Sie es nicht einfach echo $$; exec myCommandin einer Shell verwenden, in der Sie andere Befehle ausführen.
  • Sie können hierfür keine Unterschale verwenden . (echo $$; exec myCommand)kann syntaktisch besser sein als sh -c 'echo $$; exec myCommand', aber wenn Sie $$innerhalb ausführen ( ), gibt es die PID der übergeordneten Shell, nicht der Subshell selbst. Es ist jedoch die PID der Subshell, die die PID des neuen Befehls ist. Einige Schalen bieten ihre eigenen , nicht tragbaren Mechanismen für die Subshell PID zu finden, die Sie könnten für diese. Insbesondere in Bash 4 , (echo $BASHPID; exec myCommand)funktioniert.

Beachten Sie schließlich, dass einige Shells eine Optimierung ausführen, bei der sie einen Befehl ausführen, als ob execsie dies tun (dh zuerst auf das Forken verzichten), wenn bekannt ist, dass die Shell danach nichts mehr tun muss. Einige Shells versuchen dies immer dann, wenn es sich um den letzten auszuführenden Befehl handelt, während andere dies nur tun, wenn vor oder nach dem Befehl keine anderen Befehle vorhanden sind, und andere dies überhaupt nicht tun. Der Effekt ist , dass , wenn Ihr vergessen zu schreiben execund einfach zu verwenden , sh -c 'echo $$; myCommand'dann wird manchmal Sie die richtige PID geben auf einigen Systemen mit einigen Muscheln. Ich rate davon ab, sich jemals auf ein solches Verhalten zu verlassen und stattdessen immer execanzugeben, wann Sie dies benötigen.

Eliah Kagan
quelle
Bevor ich ausgeführt werden kann myCommand, muss ich in meinem Bash-Skript eine Reihe von Umgebungsvariablen festlegen. Übertragen diese in die Umgebung, in der der execBefehl ausgeführt wird?
user5359531
Es sieht so aus, als würde meine Umgebung in den execBefehl übertragen. Dieser Ansatz funktioniert jedoch nicht, wenn myCommandandere Prozesse gestartet werden, mit denen Sie arbeiten müssen. wenn ich eine Ausgabe von kill -INT <pid>wo piderhält auf diese Weise, wird das Signal nicht die Teilprozesse erreicht gestartet durch myCommand, während , wenn ich laufe myCommand in der aktuellen Sitzung und Strg + C, die Signale richtig ausbreiten.
user5359531
1
Ich habe es versucht, aber die PID des myCommand-Prozesses scheint die PID-Ausgabe von echo $$ +1 zu sein. Mache ich etwas falsch?
Crobar
Mein Befehl sieht so aus:sh -c 'echo $$; exec /usr/local/bin/mbdyn -f "input.file" -o "/path/to/outputdir" > "command_output.txt" 2>&1 &'
Crobar
7

Ich kenne keine einfachere Lösung, verwende aber nicht $! gut genug? Sie können den Wert immer einer anderen Variablen zuweisen, wenn Sie ihn später benötigen, wie von anderen gesagt.

Als Randnotiz, anstatt von ps zu leiten, könnten Sie pgrepoder verwenden pidof.

Carlpett
quelle
5

benutze exec aus einem Bash-Skript, nachdem du die PID in einer Datei registriert hast:

Beispiel:

Angenommen, Sie haben ein Skript mit dem Namen "forever.sh", das Sie mit den Argumenten p1, p2, p3 ausführen möchten

forever.sh Quellcode:

#!/bin/sh

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

erstelle eine reaper.sh:

#!/bin/sh

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

Laufen Sie forever.sh durch reaper.sh:

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

forever.sh protokolliert lediglich alle 5 Sekunden eine Zeile in syslog

Sie haben jetzt die PID in /var/run/forever.sh.pid

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

und forever.sh rennt aok. Syslog grep:

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

Sie können es in der Prozesstabelle sehen:

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
user237419
quelle
3
ach ja, und der "oneliner": / bin / sh -c 'echo $$> / tmp / my.pid && exec program args' &
user237419 24.11.10
1
Um interne Leerzeichen in den Argumenten korrekt beizubehalten, sollten Sie exec "$@"anstelle von verwenden exec $*. Technisch gesehen müssen Sie nicht das Leerzeichen, sondern das Vorkommen der Zeichen im IFS-Shell-Parameter (standardmäßig Leerzeichen, Tabulator und Zeilenvorschub) beibehalten.
Chris Johnsen
Punkt genommen. :)
user237419
Vielen Dank, ich wusste nicht $$ Parameter. Das kann sehr nützlich sein.
Rafalmag
3

In der Bash-Shell könnte eine Alternative $!die jobs -peingebaute sein. In einigen Fällen wird das !In $!von der Shell vor (oder anstelle von) der Variablenexpansion interpretiert, was zu unerwarteten Ergebnissen führt.

Das funktioniert zum Beispiel nicht:

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

während dies wird:

((yourcommand) & jobs -p >/var/run/pidfile)
mustaccio
quelle
Ich denke du meintest ((dein Befehl) & jobs -p> / var / run / pidfile).
Dom
1

Sie können Folgendes verwenden:

$ myCommand ; pid=$!

Oder

$ myCommand && pid=$!

Die beiden Befehle können Gelenke mit ;oder sein &&. Im zweiten Fall wird die pid nur gesetzt, wenn der erste Befehl erfolgreich ist. Sie können die Prozess-ID von erhalten $pid.

Khaled
quelle
3
OP möchte die PID erhalten, damit er sie später töten kann. ; und && erfordern, dass der ursprüngliche Prozess vor dem Echo $ beendet wird! ausgeführt wird.
User9517
Ja, du hast recht. Dies gibt Ihnen die PID, nachdem myCommand beendet wurde.
Khaled
6
Wenn Sie $!nach &&oder referenzieren ;, erhalten Sie niemals die PID des Prozesses, der für die linke Seite des Befehlstrennzeichens gestartet wurde. $!wird nur für asynchron gestartete Prozesse gesetzt (zB normalerweise mit, &aber einige Shells haben auch andere Methoden).
Chris Johnsen
Es ist hilfreich, und warum stimmt außer mir niemand ab? Gute Arbeit :)
Tempel
1

Dies ist eine knifflige Antwort und wird wahrscheinlich für die meisten Leute nicht funktionieren. Es ist auch ein riesiges Sicherheitsrisiko, machen Sie es also nur, wenn Sie sicher sind, dass Sie sicher sind und die Eingaben bereinigt sind, und ... Sie haben die Idee.

Kompilieren Sie das kleine C-Programm hier in eine aufgerufene Binärdatei start(oder was auch immer Sie wollen), und führen Sie Ihr Programm als aus./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;
}

Kurz gesagt, dies gibt die PID aus stdoutund lädt dann Ihr Programm in den Prozess. Es sollte immer noch die gleiche PID haben.

tonysdg
quelle
Wie kann ich die von diesem Code zurückgegebene PID-Nummer in einer Bash-Variablen aufzeichnen? Aus einem mir unklaren Grund stdoutscheint in diesem Beispiel nicht aufgezeichnet zu werden:RESULT="$(./start_and_get_pid.out echo yo)"; echo "$RESULT"
Tfb9
@ Tfb9: Ich würde ehrlich einen der anderen Ansätze empfehlen; Ich habe das vor mehr als 2 Jahren geschrieben und es ist bei weitem eine der hackigeren / fehleranfälligeren Methoden, die vorgestellt wurden (oder so denke ich).
Tonysdg
Danke für den Hinweis. Ich glaube, ich habe mein Problem damit gelöstsleep 10 & PID_IS=$!; echo $PID_IS
Tfb9