GNU/Linux >> Linux Esercitazione >  >> Linux

perché un ciclo while bash non esce durante il piping al sottocomando terminato?

È dovuto a una scelta nell'implementazione.

Eseguire lo stesso script su Solaris con ksh93 produce un comportamento diverso:

$ while /usr/bin/true ; do echo "ok" | cat ; done | exit 1
cat: write error [Broken pipe]

Ciò che fa scattare il problema è la pipeline interna, senza di essa, il ciclo termina qualunque sia la shell/OS:

$ while /usr/bin/true ; do echo "ok" ; done | exit 1
$

cat sta ricevendo un segnale SIGPIPE sotto bash ma la shell sta comunque iterando il ciclo.

Process 5659 suspended
[pid 28801] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
[pid 28801] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28801 detached
Process 28800 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
Process 28802 attached
Process 28803 attached
[pid 28803] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
Process 5659 suspended
[pid 28803] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28803 detached
Process 28802 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
Process 28804 attached
Process 28805 attached (waiting for parent)
Process 28805 resumed (parent 5659 ready)
Process 5659 suspended
[pid 28805] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
[pid 28805] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28805 detached
Process 28804 detached
--- SIGCHLD (Child exited) @ 0 (0) ---

Bash la documentazione indica:

La shell aspetta tutti i comandi nella pipeline per terminare prima di restituire un valore.

Ksh la documentazione indica:

Ogni comando, tranne forse l'ultimo, viene eseguito come un processo separato; la shell aspetta l'ultimo comando terminare.

POSIX afferma:

Se la pipeline non è in background (vedi Elenchi asincroni), la shell deve attendere l'ultimo comando specificato nella pipeline per il completamento e può anche attendere tutti i comandi da completare.


Questo problema mi ha infastidito per anni. Grazie a jilliagre per la spinta nella giusta direzione.

Ribadendo un po' la domanda, sulla mia macchina Linux, questo si chiude come previsto:

while true ; do echo "ok"; done | head

Ma se aggiungo una pipe, no esci come previsto:

while true ; do echo "ok" | cat; done | head

Questo mi ha frustrato per anni. Considerando la risposta scritta da jilliagre, ho trovato questa soluzione meravigliosa:

while true ; do echo "ok" | cat || exit; done | head

Q.E.D. ...

Beh, non proprio. Ecco qualcosa di un po' più complicato:

i=0
while true; do
    i=`expr $i + 1`
    echo "$i" | grep '0$' || exit
done | head

Questo non funziona bene. Ho aggiunto il || exit quindi sa come terminare in anticipo, ma il primissimo echo non corrisponde a grep quindi il ciclo si interrompe immediatamente. In questo caso, non sei davvero interessato allo stato di uscita del grep . La mia soluzione è aggiungere un altro cat . Quindi, ecco uno script artificioso chiamato "tens":

#!/bin/bash
i=0
while true; do
    i=`expr $i + 1`
    echo "$i" | grep '0$' | cat || exit
done

Questo termina correttamente quando viene eseguito come tens | head . Grazie a Dio.


Linux
  1. Codice di uscita predefinito al termine del processo?

  2. Esempi di Bash For Loop e While Loop

  3. Perché xargs -L produce il formato corretto, mentre xargs -n no?

  4. Usando e in Bash mentre il ciclo

  5. Perché questa pipeline di shell termina?

Bash break:come uscire da un loop

Bash mentre Loop

Bash fino a Loop

Bash Scripting - Mentre e fino a ciclo spiegato con esempi

Bash:loop fino a quando lo stato di uscita del comando è uguale a 0

Shell Script while loop:[intorno a una pipeline manca `]'