Ruft die PID eines Befehls in einer Hintergrundfolge von Befehlen ab

11

Wenn bashich in ausführe:

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

Wo cmd{1..n}kann das die PID von nicht unterscheiden cmdi? Wie kann ich alternativ den cmdiProzess signalisieren ? (Zum Beispiel ist es senden SIGUSR1?) pkill/ pgrep, pidofUsw. sieht nicht aus wie gute Antworten, da andere Instanzen cmdivielleicht läuft, unter anderem als Teil der gleichen Pipeline. jobs -pgibt die PID von cmd1für mich.

ikann alles sein in {1..n}.

muru
quelle
möglich Duplikat Wie kann ich die pid eines Prozesses erhalten begonnen auf diese Weise
G-Man sagt "wieder einzusetzen Monica
1
@ G-Man Pflege zu erklären? Ich sehe nur oberflächliche Ähnlichkeit, und wie ich in Rameshs Antwort erklärt habe, ist das Ändern des Befehlssatzes nicht sehr nützlich.
Muru
Oberflächliche Ähnlichkeit? cat /var/run/out | nc -l 8080ist nur oberflächlich ähnlich zu cmd1 | cmd2? Ihre Einschränkung, dass Sie die Bare-Bones-Pipeline eingeben und dann die PIDs wiederherstellen möchten, ist (1) in der Frage nicht angegeben und (2) es ist unwahrscheinlich, dass eine gute, allgemeine Lösung möglich ist.
G-Man sagt "Reinstate Monica"
@ G-Man Im Gegenteil, Sie legen Einschränkungen fest, die einfach nicht angegeben sind. cmd1 | cmd2ist ein ganz besonderer Fall, in dem beide PIDs leicht erhältlich sind. Habe ich etwas über n gesagt? Warum würden Sie also n = 2 annehmen? Habe ich etwas darüber gesagt, was cmdi ist? Warum sollten Sie also annehmen, dass ich cmdi ändern könnte? Ich bitte um eine allgemeine Lösung und Sie legen Beschränkungen auf.
Muru

Antworten:

6

Für die ursprüngliche Version der Frage ist die spezielle Variable $!perfekt , wenn nur die PID des letzten Befehls gewünscht wurde .

foo | bar | baz &
baz_pid=$!

Es gibt keinen ähnlich einfachen Zugriff auf die PIDs der anderen Prozesse.

Es hat lange gedauert, bis $pipestatus(zsh) und $PIPESTATUS(bash) hinzugefügt wurden, sodass wir endlich Zugriff auf alle Exit-Status in einer Pipeline haben, zusätzlich zu $?dem letzten, der seit der ursprünglichen Bourne-Shell verfügbar war. Vielleicht passiert $!irgendwann etwas Analoges .


quelle
Würde es Ihnen etwas ausmachen, wenn ich die Frage so bearbeite, dass sie auch nach der PID eines beliebigen Befehls in der Liste fragt? Oder soll ich eine neue Frage stellen?
Muru
Sie müssen wahrscheinlich viel länger auf eine Antwort auf diese Frage warten. Ich habe keine starken Gefühle bezüglich der Organisation der Stackexchange-Site, also separate Frage, Frage bearbeiten, was auch immer ... wird mich nicht stören
Das ist okay, das unmittelbare Problem ist gelöst, jetzt ist die Neugierde verantwortlich. Ich werde es dann bearbeiten. Nur ein Heads-up, da sich die Fragen drastisch geändert haben und die älteren Antworten sehr fehl am Platz sind.
Muru
@muru - beachte, dass es besser gewesen wäre, ein neues Q zu erstellen, das auf dieses verweist.
slm
@slm ordnungsgemäß vermerkt. Wird dies in Zukunft tun.
Muru
4

Ich denke, Sie könnten etwas tun, wie hier vorgeschlagen .

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

Hier im obigen Beispiel habe ich die PID des dritten Pipeline-Prozesses abgerufen und bis zur Datei-PID notiert. Ich konnte es für jeden Rohrleitungsprozess notieren.

Ramesh
quelle
Interessant, aber dies würde das Ändern des Befehlssatzes beinhalten. Nicht viel Verwendung, sobald die Befehle ausgeführt wurden.
Muru
@muru - was? Was nützt eine PID, wenn sie ausgeführt wurde? Möchten Sie die PID der Pipeline? jobs -p. signalisiere es mit SIGPIPE. Willst du cmdi- das.
Mikeserv
1
@mikeserv Nicht, wenn sie im Hintergrund sind und laufen, während wir sprechen. Durch welche Zauberei soll ich die Kommandozeile dafür ändern?
Muru
1
@muru das wäre eine Zauberei. Sie benötigen einen Debugger.
Mikeserv
Ich finde, dass dies ein nützliches Muster ist, um Hintergrundprozesse zu starten, darauf zu warten, dass sie einen bestimmten Zustand erreichen, und sie dann zu töten. Falls jemand interessiert ist: gist.github.com/MatrixManAtYrService/…
MatrixManAtYrService
2

Eine nicht sehr portable, Linux-spezifische Lösung könnte darin bestehen, die Prozesse mithilfe der Pipes zu verfolgen, die sie verbinden. Wir können die PIDs der Befehle first ( jobs -p) und last ( $!) in der Pipeline abrufen . Mit beiden PIDs könnte dieses Skript folgende Aufgabe übernehmen:

#! /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
quelle
Für Neugierige gibt es hier mehr über solche
MatrixManAtYrService
0

Ich verwende hier in diesem Code nullbasierte Arrays. Sei einfach vorsichtig, woran du vorbeigehst 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
quelle