Perché “BrokenPipeError” dipende dalla dimensione del flusso in pipe?
Su Febbraio 15, 2021 da admin Il seguente script solleva BrokenPipeError: [Errno 32] Broken pipe
, quando reindirizzato a un comando come head
(a meno che il numero di righe da intestare non superi il numero di righe stampate dallo script Python).
for i in range(16386): print("")
$ python test-pipe.py | head -1 Traceback (most recent call last): File "test-pipe.py", line 2, in <module> print("") BrokenPipeError: [Errno 32] Broken pipe
La mia comprensione (da questa risposta e le risposte a questa domanda ) è che viene generato un errore se una pipe viene chiusa prima che il processo Python sia terminato scrivendoci.
Tuttavia, se decremento lintervallo iterato con uno a 16385, lerrore non viene sollevato (non sono sicuro che questa soglia sia la stessa su tutte le macchine quindi potrei provare solo un numero alto e basso riprodurre). Inizialmente pensavo che questo potesse essere correlato alla dimensione del buffer del pipe, ma per me è 64 KB (secondo M=0; while printf A; do >&2 printf "\r$((++M)) B"; done | sleep 999
, quindi non sembra essere questo il motivo.
Perché loccorrenza BrokenPipeError
dipende dalla dimensione di ciò che viene trasmesso?
Questo è con Python 3.8.1 su Linux 5.4.15-arch1 -1.
Commenti
Risposta
Perché loccorrenzaBrokenPipeError dipende dalle dimensioni di ciò che viene convogliato?
Perché scrivere più cose richiede più tempo e il lato destro della pipeline potrebbe morire prima che il tuo python abbia finito di scriverlo. Inoltre, se il python cerca di scrivere più di quanto rientra nel buffer della pipe, bloccherà e darà a head -1
ampio tempo per uscire.
Poiché head -1
richiede un po di tempo per vivere e morire, il python potrebbe usare quel tempo per scrivere tutte le sue cose – se si inserisce nel buffer del pipe – ed uscire con successo. È difficile da prevedere, poiché il kernel è libero di programmare entrambi i lati della pipeline in qualsiasi ordine e potrebbe ritardare lavvio di head -1
finché lo ritiene opportuno, oppure potrebbe fermalo in qualsiasi momento.
>>> python3 -c "for i in range(50000): print("")" | sleep .01 Traceback (most recent call last): File "<string>", line 1, in <module> BrokenPipeError: [Errno 32] Broken pipe >>> python3 -c "for i in range(50000): print("")" | sleep .1 # OK!
Ma se Python cerca di scrivere più cose di quante ne stia nel pipe, otterrà inesorabilmente un EPIPE
o SIGPIPE
alla fine, indipendentemente da quanto tempo ha:
>>> python3 -c "for i in range(100000): print("")" | sleep 20 Traceback (most recent call last): File "<string>", line 1, in <module> BrokenPipeError: [Errno 32] Broken pipe
ma per me sono 64 KB … quindi non sembra essere questo il motivo.
Tieni presente che python utilizza il buffering completo quando loutput non è un terminale; non scrive riga per riga o byte per byte, ma per blocchi di qualche dimensione:
>>> strace -s3 -e trace=write python3 -c "for i in range(50000): print("")" | sleep .3 write(1, "\n\n\n"..., 8193) = 8193 write(1, "\n\n\n"..., 8193) = 8193 write(1, "\n\n\n"..., 8193) = 8193 write(1, "\n\n\n"..., 8193) = 8193 write(1, "\n\n\n"..., 8193) = 8193 write(1, "\n\n\n"..., 8193) = 8193 write(1, "\n\n\n"..., 842) = 842 +++ exited with 0 +++
:
invece del comandohead -1
.python3 -c "for i in range(10000): print(i)" | : </dev/tcp/unix.stackexchange.com/https
. Se raddoppi / tripli lintervallo, il pitone fallisce con Broken Pipe.