De ce „BrokenPipeError” depinde de dimensiunea fluxului conductat?
On februarie 15, 2021 by admin Următorul script ridică BrokenPipeError: [Errno 32] Broken pipe
, când este direcționat către o comandă precum head
(cu excepția cazului în care numărul de linii pentru cap depășește numărul de linii tipărite de scriptul 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
Înțelegerea mea (din acest răspuns și răspunsurile la această întrebare ) este că se ridică o eroare dacă o conductă este închisă înainte de finalizarea procesului Python scriindu-i.
Cu toate acestea, dacă scad intervalul iterat cu unu la 16385, eroarea nu este ridicată (nu sunt sigur dacă acest prag este același pe toate mașinile, așa că poate încercați doar un număr mare și mic a reproduce). Am crezut inițial că acest lucru ar putea fi legat de dimensiunea bufferului de țeavă, dar asta este 64K pentru mine (conform M=0; while printf A; do >&2 printf "\r$((++M)) B"; done | sleep 999
, deci nu pare să fie motivul.
De ce apariția BrokenPipeError
depinde de mărimea a ceea ce este canalizat?
Acest lucru se întâmplă cu Python 3.8.1 pe Linux 5.4.15-arch1 -1.
Comentarii
Răspuns
De ce este occurrenceBrokenPipeError dependentă de mărimea a ceea ce este canalizat?
Pentru că scrierea mai multor lucruri necesită mai mult timp, iar partea dreaptă a conductei poate să moară înainte ca python-ul dvs. să termine de scris. De asemenea, dacă python încearcă să scrie mai mult decât se potrivește în bufferul de țevi, acesta va bloca și va oferi head -1
suficient timp pentru a ieși.
Deoarece head -1
durează ceva timp pentru a trăi și a muri, python poate folosi acel timp pentru a scrie toate lucrurile sale – dacă se potrivește în bufferul de țeavă – și a ieși cu succes. Acest lucru este greu de previzionat, deoarece nucleul este liber să programeze ambele părți ale conductei în orice ordine și poate întârzia pornirea head -1
atâta timp cât consideră necesar, sau poate oprește-l în orice moment.
>>> 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!
Dar dacă Python încearcă să scrie mai multe lucruri decât se potrivește în conductă, va primi inexorabil un EPIPE
sau SIGPIPE
în cele din urmă, indiferent de cât timp are:
>>> 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
dar asta înseamnă 64K pentru mine … așa că nu pare să fie motivul.
Rețineți că python folosește buffering complet când ieșirea nu este un terminal; nu scrie linie cu linie sau octet cu octet, ci cu bucăți de unele dimensiuni:
>>> 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 +++
:
în locul comenziihead -1
.python3 -c "for i in range(10000): print(i)" | : </dev/tcp/unix.stackexchange.com/https
. Dacă dublezi / triplezi intervalul, pitonul cu eșec cu Broken Pipe.