Un programma successivo in una pipeline può vedere il codice di uscita del programma precedente?


8

Vorrei creare una pipeline di script Bash come questa

prog1 | prog2

tale che prog2 può vedere il codice di uscita di prog1 e agire in modo diverso in base a tali informazioni.

È possibile?


Potresti elaborare l' atto in modo diverso come parte della tua domanda?
Devnull

Hai un controllo limitato su quanto prog2è progredito quando prog1esce, a causa del buffering interno utilizzato per implementare la pipe e come prog1e prog2sono programmati.
Chepner,

Guarda anche [In quale ordine vengono eseguiti i comandi piped?] (Unix.stackexchange.com/q/37508)
DK Bose

Risposte:


4

La risposta generale è no. È possibile prog2uscire prima prog1ancora di iniziare (ovviamente ciò non può accadere se in prog2realtà legge alcuni input, che ti aspetteresti che faccia se lo stai usando in una pipeline). È sicuramente possibile prog2uscire prima prog1; questo accade ad esempio quando prog2è un programma di ricerca che esce non appena trova una corrispondenza, nel qual caso prog1potrebbe non aver ancora finito di produrre tutti i dati.

Non esiste un modo diretto per prog2recuperare lo stato di uscita prog1o persino sapere che prog1è uscito. Tutto ciò che prog2può sapere è che prog1ha chiuso la sua estremità del tubo, cosa che può fare senza morire.

Se si desidera ottenere lo stato di uscita prog1da prog2, esistono due metodi comuni: è possibile scriverlo in un file oppure è possibile inviarlo tramite la pipe. È possibile inviare lo stato dell'uscita come ultima riga dei dati di piping. Devi assicurarti di non elaborare l'ultima riga finché non sai che è l'ultima riga, cioè fino a quando non hai provato a leggere la riga successiva.

{ prog1; echo $?; } | 

Ecco un esempio in cui il lato destro è un filtro di testo che colora ogni riga contenente la parola "errore" in rosso. Se il lato sinistro non funziona, il lato destro esce con lo stesso stato.

{ prog1; echo $?; } | awk '
    NR != 1 {
        if (line ~ /[Ee][Rr][Rr][Oo][Rr]/) print "\033[31m" line "\033[0m";
        else print line;
    }
    {line = $0}
    END {exit($0)}
'

Ci stavo provando in { command; echo ${PIPESTATUS[@]}; } | sort | ...modo che lo stato di uscita arrivasse per primo nello stream. È tutto molto interessante!

@ illuminÉ Funzionerebbe solo se ${PIPESTATUS[@]}fosse ordinato prima di ogni altra cosa nell'output di command. Se commandstampa un gruppo di numeri o se riesce a stampare un testo arbitrario, sei nei guai: non sarai in grado di distinguerne l'output dalla riga di stato.
Gilles 'SO- smetti di essere malvagio'

Grazie, in effetti riesce a ordinare uno stato di successo solo se il comando non contiene 0 nel suo output lol. Tgif / s.

2

Anche se puoi in alcuni casi speciali (vedi le altre risposte) non puoi in tutti i casi. Alcuni programmi di filtro continueranno a funzionare, mentre altri manterranno tutto l'output, lo scateneranno in una sola esplosione e poi usciranno.

Per un esempio di un programma "continua così", grepsarà server, come farebbe tail -f /var/log/some_log_file. L'uso sortin una pipeline provoca uno "stallo", in quanto sortraccoglierà l'input fino alla chiusura del tubo di fronte. L'utilizzo xargsaggiunge un'ulteriore complicazione: i programmi avviati da xargs(potrebbe avviare molte istanze) fanno parte della pipeline o no?


-1 perché hai votato per aver razionalizzato una risposta errata.
ctrl-alt-delor

@richard Huh? La risposta di Bruce è corretta (anche se il secondo paragrafo è un po 'confuso).
Gilles 'SO-smetti di essere malvagio' il

1

La risposta: non direttamente.

@terdon ha illustrato che il codice di uscita del comando precedente nella pipe deve essere inviato come comando esplicito al comando successivo.

Ricorda che la pipe è semplicemente una mappatura dello STDOUT del comando precedente con lo STDIN del comando successivo; i codici di uscita non vengono inviati a STDOUT (o STDERR).


1
-1 perché hai votato per aver citato una risposta errata e non hai detto molto altro.
ctrl-alt-delor

@richard abbastanza giusto ... Avrei dovuto ricontrollare ... ecco cosa è successo se mi costringo a rispondere a una domanda quando sono molto stanco ...
pepoluan

1

Tutto il processo, in fase di pipeline, viene avviato prima di qualsiasi uscita. Pertanto, prog2potrebbe essere necessario ottenere queste informazioni dopo l'avvio, nonché interrompere l'elaborazione fino a quando non prog1fosse terminata, ciò potrebbe arrestare la pipa. Sembra che ci siano problemi fondamentali nel fare ciò che chiedi, non limitazioni del sistema operativo.

Probabilmente devi considerare un file temporaneo o inserire il risultato in una variabile.

Esempio per una piccola quantità di dati, usando una variabile.

tmp=$(prog1)
if test "z$PIPESTATUS" == "z0"
then
   
else
   
fi

C'è un vuoto nel tuo ragionamento. prog2viene avviato prima del prog1completamento in generale, ma potrebbe esserci un modo per ricevere lo stato dell'output prog1mentre è in esecuzione.
Gilles 'SO- smetti di essere malvagio' il

0

Per finire la risposta di Gilles ,

(prog1; echo $? > /tmp/prog1.status) | prog2

è un approccio.  prog2potrebbe neanche

  • leggere l'input standard fino alla fine, quindi leggere /tmp/prog1.statuso
  • verificare l'esistenza di /tmp/prog1.statusperiodicamente durante la lettura dell'input standard.
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.