Comment se faire pid du processus qui vient de commencer

71

Je veux commencer le processus (par exemple. MyCommand) et obtenir son pid (pour permettre de le tuer plus tard).

J'ai essayé ps et filtrer par nom, mais je ne peux pas distinguer processus par noms

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

Parce que les noms de processus ne sont pas uniques.

Je peux exécuter le processus par:

myCommand &

J'ai trouvé que je pouvais obtenir ce PID en:

echo $!

Y a-t-il une solution plus simple?

Je serais heureux d'exécuter myCommand et d'obtenir son PID à la suite d'une commande d'une ligne.

Rafalmag
la source

Réponses:

77

Quoi de plus simple que echo $!? En une ligne:

myCommand & echo $!
Jacek Konieczny
la source
Merci de fusionner ces commandes avec "&" m'a beaucoup aidé.
Rafalmag
8
dans un script bash, dans une boucle qui lance des programmes, $! n'est pas précis. Parfois, il retourne le pid du script lui-même, parfois de grep ou awk, le script utilise-t-il une solution quelconque pour obtenir spécifiquement le pid du processus qui vient d'être lancé dans ce scénario? quelque chose comme pid = myprogramaurait été génial
2
Notez que pour cela, vous devez démarrer la commande en utilisant &la ligne précédente, sinon, l'écho renvoie un blanc.
rogerdpack
2
assigner à une variable comme command & echo $!gèle l'exécution à cette étape :(
Shashank Vivek
26

Envelopper la commande dans un petit script

#!/bin/bash
yourcommand &
echo $! >/path/to/pid.file
user9517 prend en charge GoFundMonica
la source
20

Vous pouvez utiliser sh -cet execpour obtenir le PID de la commande avant même son exécution.

Pour commencer myCommand, afin que son PID soit imprimé avant son exécution, vous pouvez utiliser:

sh -c 'echo $$; exec myCommand'

Comment ça fonctionne:

Cela démarre un nouveau shell, affiche le PID de ce shell, puis utilise la commande execintégrée pour remplacer le shell par votre commande, en veillant à ce qu'il ait le même PID. Lorsque votre shell exécute une commande avec la execcommande intégrée, votre shell est en fait de plus cette commande , plutôt que le comportement plus fréquent de bifurquer une nouvelle copie de lui - même, qui a son propre PID séparé et qui devient alors la commande.

Je trouve cela beaucoup plus simple que des alternatives impliquant une exécution asynchrone (avec &), un contrôle des travaux ou une recherche avec ps. Ces approches sont acceptables, mais à moins que vous n'ayez une raison spécifique de les utiliser - par exemple, la commande est peut-être déjà en cours d'exécution. Dans ce cas, il serait logique de rechercher son PID ou d'utiliser le contrôle des tâches - je suggère de commencer par cette méthode. (Et je n’envisagerais certainement pas d’écrire un script complexe ou un autre programme pour y parvenir).

Cette réponse comprend un exemple de cette technique.


Des parties de cette commande peuvent parfois être omises, mais pas habituellement.

Même si le shell que vous utilisez est de type Bourne et prend donc en charge les fonctions execintégrées avec ces sémantiques, vous ne devriez généralement pas éviter d'utiliser sh -c(ou l'équivalent) pour créer un nouveau processus de shell distinct à cette fin, car:

  • Une fois que le shell est devenu myCommand, il n'y a plus de shell en attente pour exécuter les commandes suivantes. sh -c 'echo $$; exec myCommand; foone serait pas en mesure d'essayer de courir fooaprès s'être remplacé avec myCommand. Sauf si vous écrivez un script qui l'exécute en tant que dernière commande, vous ne pouvez pas l'utiliser uniquement echo $$; exec myCommanddans un shell où vous exécutez d'autres commandes.
  • Vous ne pouvez pas utiliser un sous - shell pour cela. (echo $$; exec myCommand)Peut être syntaxiquement plus beau que sh -c 'echo $$; exec myCommand', mais quand vous courez $$dedans ( ), ça donne le PID du shell parent, pas du sous-shell lui-même. Mais c'est le PID du sous-shell qui sera le PID de la nouvelle commande. Certains shells fournissent leurs propres mécanismes non portables pour trouver le PID du sous-shell, que vous pouvez utiliser pour cela. En particulier, dans Bash 4 , (echo $BASHPID; exec myCommand)fonctionne.

Enfin, notez que certains shells effectuent une optimisation en exécutant une commande comme si exec(c’est-à-dire qu’ils renonçaient au premier abord) quand on sait que le shell n’aura plus rien à faire par la suite. Certains shells essaient de le faire chaque fois que c'est la dernière commande à être exécutée, tandis que d'autres ne le feront que lorsqu'il n'y a pas d'autres commandes avant ou après la commande, et d'autres ne le feront pas du tout. L'effet est que si vous oubliez d'écrire execet utilisez simplement, sh -c 'echo $$; myCommand'vous obtiendrez parfois le bon PID sur certains systèmes avec certains shells. Je recommande de ne jamais compter sur un tel comportement , mais de toujours inclure execquand c'est ce dont vous avez besoin.

Eliah Kagan
la source
Avant de pouvoir exécuter myCommandmon travail, je dois définir un certain nombre de variables d’environnement dans mon script bash. Celles-ci seront-elles transférées à l'environnement dans lequel la execcommande est exécutée?
user5359531
On dirait que mon environnement est reporté dans la execcommande. Cependant, cette approche ne fonctionne pas au myCommanddémarrage d'autres processus, ceux avec lesquels vous devez travailler. lorsque je produis un a kill -INT <pid>où a pidété obtenu de cette manière, le signal n'atteint pas les sous-processus démarrés par myCommand, alors que si je cours myCommand dans la session en cours et en Ctrl + C, les signaux se propagent correctement.
user5359531
1
J'ai essayé ceci, mais le pid du processus myCommand semble être la sortie du pid de echo $$ +1. Est-ce que je fais quelque chose de mal?
Crobar
Ma commande ressemble à ceci:sh -c 'echo $$; exec /usr/local/bin/mbdyn -f "input.file" -o "/path/to/outputdir" > "command_output.txt" 2>&1 &'
crobar
7

Je ne connais pas de solution plus simple, mais je n’utilise pas $! assez bien? Vous pouvez toujours affecter la valeur à une autre variable si vous en avez besoin ultérieurement, comme d'autres l'ont dit.

En guise d’accompagnement, vous pouvez utiliser pgrepou pidof.

Carlpett
la source
5

utilisez exec depuis un script bash après avoir enregistré le pid dans un fichier:

exemple:

supposons que vous ayez un script nommé "forever.sh" que vous voulez exécuter avec les arguments p1, p2, p3

code source de forever.sh:

#!/bin/sh

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

créer un reaper.sh:

#!/bin/sh

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

exécutez forever.sh via reaper.sh:

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

forever.sh ne fait rien de plus que consigner une ligne dans syslog toutes les 5 secondes

vous avez maintenant le pid dans /var/run/forever.sh.pid

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

et forever.sh exécute aok. syslog grep:

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

vous pouvez le voir dans la table de processus:

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
utilisateur237419
la source
3
oh, et le "oneliner": / bin / sh -c 'echo $$> / tmp / my.pid && exec programme args' &
user237419
1
Pour conserver correctement les espaces internes dans les arguments, vous devez utiliser à la exec "$@"place de exec $*. Techniquement, vous ne devez pas conserver les espaces, mais les occurrences des caractères dans le paramètre du shell IFS (qui utilise par défaut les espaces, les tabulations et les retours à la ligne).
Chris Johnsen
point pris. :)
user237419
Merci, je ne connaissais pas le paramètre $$. Cela peut être très utile.
Rafalmag
3

Dans le shell bash, une alternative à $!pourrait être l' jobs -pintégré. Dans certains cas , !en $!est interprété par la coquille avant (ou au lieu de) l'expansion variable conduisant à des résultats inattendus.

Ceci, par exemple, ne fonctionnera pas:

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

alors que cela va:

((yourcommand) & jobs -p >/var/run/pidfile)
mustaccio
la source
Je pense que vous vouliez dire ((votre commande) & jobs -p> / var / run / pidfile).
Dom
1

Vous pouvez utiliser quelque chose comme:

$ myCommand ; pid=$!

Ou

$ myCommand && pid=$!

Les deux commandes peuvent être des joints utilisant ;ou &&. Dans le second cas, le pid ne sera défini que si la première commande réussit. Vous pouvez obtenir l'identifiant du processus à partir de $pid.

Khaled
la source
3
OP veut obtenir le PID pour pouvoir le tuer plus tard. ; et && exigent que le processus original se termine avant echo $! est exécuté.
user9517 prend en charge GoFundMonica
Oui, tu as raison. Cela vous donnera le pid après la fin de myCommand.
Khaled
6
Le référencement $!après &&ou ;ne vous donnera jamais le PID du processus démarré pour le côté gauche du séparateur de commandes. $!n'est défini que pour les processus lancés de manière asynchrone (par exemple, généralement avec, &mais certains shells utilisent aussi d'autres méthodes).
Chris Johnsen
c'est utile, et pourquoi personne ne vote en dehors de moi? bon travail cependant :)
temple
1

Ceci est un peu une réponse bidouille et ne fonctionnera probablement pas pour la plupart des gens. C’est également un risque énorme pour la sécurité, alors ne le faites pas à moins d’être sûr que vous serez en sécurité et que les entrées sont désinfectées et… eh bien, vous avez l’idée.

Compilez le petit programme C ici dans un binaire appelé start(ou ce que vous voulez), puis lancez votre programme en tant que./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 résumé, ceci imprimera le PID dans stdout, puis chargera votre programme dans le processus. Il devrait toujours avoir le même PID.

tonysdg
la source
Comment enregistrer le numéro PID renvoyé par ce code dans une variable bash? Pour une raison qui ne me stdoutsemble pas claire, ne semble pas être enregistré dans cet exemple:RESULT="$(./start_and_get_pid.out echo yo)"; echo "$RESULT"
Tfb9
@ Tfb9: Je recommanderais honnêtement l'une des autres approches; J'ai écrit ceci il y a plus de 2 ans et c'est de loin l'une des méthodes les plus hackier / sujettes aux erreurs présentées (ou du moins je pense).
tonysdg
Merci pour la note. Je pense avoir résolu mon problème avec cecisleep 10 & PID_IS=$!; echo $PID_IS
Tfb9