Ottieni il PID di qualsiasi comando nella sequenza di comandi convogliata in background


11

Se, in bash, eseguo:

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

dove il cmd{1..n}non può essere distinto, come ottengo il PID di cmdi? In alternativa, come posso segnalare il cmdiprocesso? (Ad esempio, inviarlo SIGUSR1?) pkill/ pgrep, pidofEcc. Non sembrano buone risposte, poiché altre istanze di cmdiforse in esecuzione, incluso come parte della stessa pipeline. jobs -pdà il PID di cmd1, per me.

ipuò essere qualsiasi cosa dentro {1..n}.



1
@ G-Man Care per spiegare? Vedo solo una somiglianza superficiale, e come ho spiegato nella risposta di Ramesh, modificare l'insieme di comandi non è di grande utilità.
Muru,

Somiglianza superficiale? cat /var/run/out | nc -l 8080è solo superficialmente simile a cmd1 | cmd2? Il vostro vincolo, che si desidera digitare la pipeline bare-bones e quindi ripristinare i PID, non è (1) indicato nella domanda e (2) è improbabile che consenta una buona soluzione generale.
G-Man dice "Ripristina Monica" il

@ G-Man Al contrario, stai imponendo vincoli che semplici non vengono dichiarati. cmd1 | cmd2è un caso molto speciale in cui entrambi i PID sono facilmente ottenibili. Ho detto qualcosa su n? Quindi perché dovresti assumere n = 2? Ho detto qualcosa su cos'è cmdi? Quindi, perché pensi che potrei modificare cmdi? Sto chiedendo una soluzione generale e tu stai imponendo restrizioni.
Muru,

Risposte:


6

Per la versione originale della domanda, quando si desiderava solo il PID dell'ultimo comando, la variabile speciale $!è perfetta.

foo | bar | baz &
baz_pid=$!

Non esiste un accesso facile simile ai PID degli altri processi.

Ci è voluto molto tempo per aggiungere $pipestatus(zsh) e $PIPESTATUS(bash), dandoci finalmente accesso a tutti gli stati di uscita in una pipeline, oltre a quello $?per l'ultimo che è stato presente dalla shell Bourne originale. Forse alla $!fine accadrà qualcosa di analogo .


Ti dispiacerebbe se ho modificato la domanda per chiedere anche il PID di un comando arbitrario nell'elenco? O dovrei iniziare una nuova domanda?
Muru,

Probabilmente dovrai aspettare molto di più per una risposta a quella. Non ho forti sentimenti sull'organizzazione del sito di stackexchange, quindi una domanda separata, modifica una domanda, qualunque cosa ... non mi disturberà

Va bene, il problema immediato è risolto, ora la curiosità è in carica. Lo modificherò allora. Solo un avvertimento, dal momento che ho visto le domande cambiare drasticamente e lasciando le risposte più vecchie fuori posto.
Muru,

@muru - nota che sarebbe stato meglio renderlo un nuovo Q facendo riferimento a questo.
slm

@slm debitamente notato. Lo farà in futuro.
muru,

4

Penso che potresti fare qualcosa come suggerito qui .

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

Qui nell'esempio sopra, ho recuperato il pid del terzo processo convogliato e l'ho annotato nel pid del file. Potrei annotarlo per qualsiasi processo convogliato.


Interessante, ma ciò implicherebbe la modifica dell'insieme di comandi. Non è molto utile una volta eseguiti i comandi.
Muru,

@muru - che cosa? a che serve un PID al termine dell'esecuzione? vuoi il PID della pipeline? jobs -p. segnalalo con SIGPIPE. Vuoi cmdi- questo.
Mikeserv,

1
@mikeserv Non se sono in background, in esecuzione mentre parliamo. Con quale stregoneria dovrei modificare la riga di comando per quello?
Muru,

1
@muru sarebbe una stregoneria. hai bisogno di un debugger.
Mikeserv,

Trovo che questo sia uno schema utile per avviare processi in background, aspettando che raggiungano un certo stato e poi li uccidano. Nel caso qualcuno fosse interessato: gist.github.com/MatrixManAtYrService/…
MatrixManAtYrService

2

Una soluzione non molto portatile, specifica per Linux, potrebbe essere quella di tracciare i processi usando le pipe che li collegano. Siamo in grado di ottenere i PID dei comandi first ( jobs -p) e last ( $!) nella pipeline. Usando uno dei PID, questo script potrebbe fare il lavoro:

#! /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

Per i curiosi, c'è di più su questo genere di cose qui: unix.stackexchange.com/a/486233/146169
MatrixManAtYrService

0

Uso array a base zero qui in questo codice. Fai solo attenzione a cosa corri 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
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.