Dlaczego parametr „BrokenPipeError” zależy od rozmiaru potokowanego strumienia?
On 15 lutego, 2021 by admin Poniższy skrypt podnosi BrokenPipeError: [Errno 32] Broken pipe
po przesłaniu potoku do polecenia, takiego jak head
(chyba że liczba wierszy do nagłówka przekracza liczbę wierszy drukowanych przez skrypt Pythona).
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
Rozumiem (z ta odpowiedź i odpowiedzi na to pytanie ) wskazują na błąd, jeśli potok zostanie zamknięty przed zakończeniem procesu w Pythonie pisząc do niego.
Jednakże, jeśli zmniejszę iterowany zakres z 1 do 16385, błąd nie zostanie podniesiony (nie jestem pewien, czy ten próg jest taki sam na wszystkich maszynach, więc może po prostu spróbuj wysokiej i niskiej liczby powielać). Początkowo myślałem, że może to być związane z rozmiarem bufora potoku, ale dla mnie jest to 64 KB (zgodnie z M=0; while printf A; do >&2 printf "\r$((++M)) B"; done | sleep 999
, więc to nie wydaje się być przyczyną.
Dlaczego wystąpienie BrokenPipeError
zależy od rozmiaru przesyłanego potoku?
Dotyczy to Pythona 3.8.1 w systemie Linux 5.4.15-arch1 -1.
Komentarze
Answer
Dlaczego wystąpienieBrokenPipeError zależy od rozmiaru przesyłanego potoku?
Ponieważ pisanie większej ilości rzeczy zajmuje więcej czasu, a prawa strona potoku może umrzeć, zanim twój Python skończy pisać. Ponadto, jeśli Python spróbuje napisać więcej, niż mieści się w buforze potoku, zablokuje i da head -1
wystarczająco dużo czasu na wyjście.
Ponieważ head -1
zajmuje trochę czasu, aby żyć i umierać, Python może wykorzystać ten czas na napisanie całej zawartości – jeśli mieści się w buforze potoku – i pomyślnie zakończyć działanie. Trudno to przewidzieć, ponieważ jądro może planować obie strony potoku w dowolnej kolejności i może opóźniać rozpoczęcie head -1
tak długo, jak uzna to za stosowne, lub może zatrzymaj go w dowolnym momencie.
>>> 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!
Ale jeśli Python spróbuje napisać więcej rzeczy, niż mieści się w potoku, nieuchronnie otrzyma EPIPE
lub SIGPIPE
na końcu, bez względu na to, ile ma czasu:
>>> 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
ale to dla mnie 64 KB … więc to nie wydaje się być powodem.
Należy pamiętać, że python używa pełnego buforowania , gdy wyjściem nie jest terminal; nie „nie pisze linia po linii ani bajt po bajcie, ale fragmentami pewnego rozmiaru:
>>> 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 +++
:
zamiast poleceniahead -1
.python3 -c "for i in range(10000): print(i)" | : </dev/tcp/unix.stackexchange.com/https
. Jeśli podwoisz / potroisz zakres, Python z niepowodzeniem z Broken Pipe.