Obtenez le PID de n'importe quelle commande dans une séquence de commandes en arrière-plan

11

Si, en bash, j'exécute:

cmd1 | cmd2 | ... | cmdi | ... | cmdn &

où le cmd{1..n}peut ne pas être distinct, comment puis-je obtenir le PID de cmdi? Sinon, comment puis-je signaler le cmdiprocessus? (Par exemple, envoyez-le SIGUSR1?) pkill/ pgrep, pidofEtc. ne semblent pas être de bonnes réponses, car d'autres instances de cmdipeut - être en cours d'exécution, y compris dans le cadre du même pipeline. jobs -pdonne le PID de cmd1, pour moi.

ipeut être n'importe quoi {1..n}.

muru
la source
possible doublon de Comment puis-je obtenir le pid d'un processus démarré de cette façon
G-Man dit 'Reinstate Monica'
1
@ G-Man Care pour expliquer? Je ne vois qu'une similitude superficielle, et comme je l'ai expliqué dans la réponse de Ramesh, la modification de l'ensemble des commandes n'est pas très utile.
muru
Similitude superficielle? cat /var/run/out | nc -l 8080est seulement superficiellement similaire à cmd1 | cmd2? Votre contrainte, que vous souhaitiez taper le pipeline bare-bones puis récupérer les PID, n'est (1) pas indiquée dans la question, et (2) est peu susceptible de permettre une bonne solution générale.
G-Man dit `` Réintègre Monica '' le
@ G-Man Au contraire, vous imposez des contraintes simples qui ne sont pas énoncées. cmd1 | cmd2est un cas très spécial où les deux PID sont facilement accessibles. Ai-je dit quelque chose sur n? Alors pourquoi supposeriez-vous que n = 2? Ai-je dit quoi que ce soit cmdi? Alors pourquoi supposeriez-vous que je pourrais modifier cmdi? Je demande une solution générale et vous imposez des restrictions.
muru

Réponses:

6

Pour la version originale de la question, lorsque seul le PID de la dernière commande était souhaité, la variable spéciale $!est parfaite.

foo | bar | baz &
baz_pid=$!

Il n'y a pas d'accès facile similaire aux PID des autres processus.

L' ajout de $pipestatus(zsh) et $PIPESTATUS(bash) a pris beaucoup de temps , nous donnant enfin accès à tous les états de sortie dans un pipeline, en plus $?du dernier qui existe depuis le shell Bourne d'origine. Peut-être que quelque chose d'analogue se produira $!éventuellement.


la source
Cela vous dérangerait-il si je modifiais la question pour demander également le PID d'une commande arbitraire dans la liste? Ou devrais-je commencer une nouvelle question?
muru
Vous devrez probablement attendre beaucoup plus longtemps pour obtenir une réponse à celle-ci. Je n'ai pas de forts sentiments à propos de l'organisation du site stackexchange, alors séparez la question, modifiez la question, peu importe ... ne me dérangera pas
Ça va, le problème immédiat est résolu, maintenant la curiosité est aux commandes. Je vais l'éditer ensuite. Juste un avertissement, car j'ai vu les questions changer radicalement et laisser les réponses plus anciennes très hors de propos.
muru
@muru - notez qu'il aurait été préférable d'en faire un nouveau Q référençant celui-ci.
slm
@slm dûment noté. Le fera à l'avenir.
muru le
4

Je pense que vous pourriez faire quelque chose comme suggéré ici .

(ls -l | echo "Hello" | df -h & echo $! >&3 ) 3>pid

Ici, dans l'exemple ci-dessus, j'ai récupéré le pid du troisième processus canalisé et l'ai noté dans le fichier pid. Je pourrais le noter pour tout processus canalisé.

Ramesh
la source
Intéressant, mais cela impliquerait de modifier l'ensemble des commandes. Pas très utile une fois les commandes exécutées.
muru
@muru - quoi? à quoi sert un PID une fois son exécution terminée? voulez-vous le PID du pipeline? jobs -p. signaler avec SIGPIPE. Voulez-vous cmdi- ceci.
mikeserv
1
@mikeserv Pas s'ils sont en arrière-plan, fonctionnant au moment où nous parlons. Par quelle sorcellerie suis-je censé modifier la ligne de commande pour cela?
muru
1
@muru ce serait une sorcellerie. vous avez besoin d'un débogueur.
mikeserv
Je trouve que c'est un modèle utile pour démarrer des processus d'arrière-plan, attendre qu'ils atteignent un certain état, puis les tuer. Dans le cas où quelqu'un est intéressé: gist.github.com/MatrixManAtYrService/...
MatrixManAtYrService
2

Une solution peu portable et spécifique à Linux pourrait être de suivre les processus à l'aide des canaux qui les connectent. Nous pouvons obtenir les PID des commandes first ( jobs -p) et last ( $!) dans le pipeline. En utilisant l'un ou l'autre PID, ce script pourrait faire le travail:

#! /bin/bash

PROC=$1
echo $PROC

if [[ $(readlink /proc/$PROC/fd/1) =~ ^pipe: ]]
then
    # Assuming first process in chain...
    NEXT_FD=1
elif [[ $(readlink /proc/$PROC/fd/0) =~ ^pipe: ]]
then
    # Last process in chain...
    NEXT_FD=0
else
    # Doesn't look like a pipe.
    exit
fi

NEXT_PROC_PIPE=$(readlink /proc/$PROC/fd/$NEXT_FD)

while [[ $NEXT_PROC_PIPE =~ ^pipe: ]] 
do
    PROC=$(find /proc/*/fd -type l -printf "%p/%l\n" 2>/dev/null | awk -F'/' '($6 == "'"$NEXT_PROC_PIPE"'") && ($3 != "'$PROC'" ) {print $3}')
    NEXT_PROC_PIPE=$(readlink /proc/$PROC/fd/$NEXT_FD)
    echo $PROC
done
muru
la source
Pour les curieux, il y a plus sur ce genre de chose ici: unix.stackexchange.com/a/486233/146169
MatrixManAtYrService
0

J'utilise des tableaux basés sur zéro ici dans ce code. Faites juste attention à ce que vous dirigez eval.

#!/bin/bash

cmd=('sleep 10' 'sleep 2' 'sleep 5')
first=1
for c in "${cmd[@]}"; do
  ((first)) && { pipe=$c; first=0; } || pipe+='|'$c
done
shopt -u lastpipe
eval $pipe &

printf 'Pipe:\n%s\n\n' "$pipe"

shellpid=$BASHPID
parent=$(ps -o pid= --ppid $shellpid | head -n -1)
declare -a pids=()
mapfile -t pids < <(printf '%s\n' $(ps -o pid= --ppid $parent))
printf '%s\n' 'Listing the arrays:'
printf '%2s %6s %s\n' i PID command
for i in "${!cmd[@]}"; do
    printf '%2d %6d %s\n' "$i" "${pids[i]}" "${cmd[i]}"
done

printf '\n%s\n' 'ps listing:'
ps xao pid,ppid,command | head -n 1
ps xao pid,ppid,command | tail | head -n -3
jarno
la source