IOError: [Errno 32] Pipa rusak: Python

91

Saya memiliki skrip Python 3 yang sangat sederhana:

f1 = open('a.txt', 'r')
print(f1.readlines())
f2 = open('b.txt', 'r')
print(f2.readlines())
f3 = open('c.txt', 'r')
print(f3.readlines())
f4 = open('d.txt', 'r')
print(f4.readlines())
f1.close()
f2.close()
f3.close()
f4.close()

Tapi selalu tertulis:

IOError: [Errno 32] Broken pipe

Saya melihat di internet semua cara rumit untuk memperbaikinya, tetapi saya menyalin kode ini secara langsung, jadi saya pikir ada yang salah dengan kode dan bukan SIGPIPE Python.

Saya mengarahkan output, jadi jika skrip di atas bernama "open.py", maka perintah saya untuk menjalankan adalah:

open.py | othercommand
JOHANNES_NYÅTT
sumber
@squiguy baris 2:print(f1.readlines())
JOHANNES_NYÅTT
2
Anda punya dua operasi IO yang terjadi di baris 2: baca dari a.txtdan tulis ke stdout. Mungkin coba pisahkan menjadi baris terpisah sehingga Anda dapat melihat operasi mana yang memicu pengecualian. Jika stdoutpipa dan ujung baca telah ditutup, maka itu bisa menjelaskan EPIPEkesalahan.
James Henstridge
1
Saya dapat mereproduksi kesalahan ini pada keluaran (mengingat kondisi yang tepat), jadi saya curiga printpanggilan tersebut adalah pelakunya. @ JOHANNES_NYÅTT, dapatkah Anda menjelaskan bagaimana Anda meluncurkan skrip Python Anda? Apakah Anda mengarahkan keluaran standar ke suatu tempat?
Blckknght
2
Ini adalah kemungkinan duplikat dari pertanyaan berikut: stackoverflow.com/questions/11423225/…

Jawaban:

48

Saya belum mereproduksi masalah, tetapi mungkin metode ini akan menyelesaikannya: (menulis baris demi baris stdoutdaripada menggunakan print)

import sys
with open('a.txt', 'r') as f1:
    for line in f1:
        sys.stdout.write(line)

Anda bisa menangkap pipa yang rusak? Ini menulis file ke stdoutbaris demi baris sampai pipa ditutup.

import sys, errno
try:
    with open('a.txt', 'r') as f1:
        for line in f1:
            sys.stdout.write(line)
except IOError as e:
    if e.errno == errno.EPIPE:
        # Handle error

Anda juga perlu memastikan bahwa othercommandmembaca dari pipa sebelum menjadi terlalu besar - /unix/11946/how-big-is-the-pipe-buffer

Alex L.
sumber
7
Meskipun ini adalah praktik pemrograman yang baik, saya pikir ini tidak ada hubungannya dengan kesalahan pipa rusak yang didapat penanya (yang mungkin ada hubungannya dengan printpanggilan, bukan dengan membaca file).
Blckknght
@Blckknght Saya menambahkan beberapa pertanyaan dan metode alternatif dan berharap untuk beberapa umpan balik dari penulis. Jika masalahnya adalah mengirim data dalam jumlah besar dari file terbuka langsung ke pernyataan cetak, maka mungkin salah satu alternatif di atas akan memperbaikinya.
Alex L
(Solusi paling sederhana seringkali adalah yang terbaik - kecuali jika ada alasan tertentu untuk memuat seluruh file lalu mencetaknya, lakukan dengan cara yang berbeda)
Alex L
1
Pekerjaan luar biasa dalam memecahkan masalah ini! Meskipun saya dapat menerima jawaban ini begitu saja, saya dapat menghargai ini hanya setelah melihat bagaimana jawaban lain (dan pendekatan saya sendiri) memucat dibandingkan dengan jawaban Anda.
Jesvin Jose
117

Masalahnya karena penanganan SIGPIPE. Anda dapat mengatasi masalah ini menggunakan kode berikut:

from signal import signal, SIGPIPE, SIG_DFL
signal(SIGPIPE,SIG_DFL) 

Lihat di sini untuk latar belakang solusi ini. Jawaban yang lebih baik di sini .

akhan
sumber
14
Ini sangat berbahaya, seperti yang baru saya temukan, karena jika Anda pernah mendapatkan SIGPIPE pada soket (httplib atau apa pun), program Anda akan keluar begitu saja tanpa peringatan atau kesalahan.
David Bennett
1
@DavidBennett, saya yakin penerapannya bergantung dan untuk tujuan Anda, jawaban yang diterima adalah yang benar. Ada banyak Tanya Jawab yang menyeluruh di sini untuk dilalui orang dan membuat keputusan berdasarkan informasi. IMO, untuk alat baris perintah, mungkin yang terbaik adalah mengabaikan sinyal pipa dalam banyak kasus.
akhan
1
Adakah cara untuk melakukan ini hanya untuk sementara?
Nate Glenn
2
@NateGlenn Anda dapat menyimpan penangan yang ada dan memulihkannya nanti.
akhan
3
Bisakah seseorang menjawab saya mengapa orang menganggap artikel blogspot sebagai sumber kebenaran yang lebih baik daripada dokumentasi resmi (petunjuk: buka tautan untuk melihat cara memperbaiki kesalahan pipa yang rusak dengan benar)? :)
Yurii Rabeshko
93

Untuk menghadirkan jawaban Alex L. yang bermanfaat , jawaban akhan yang membantu , dan jawaban membantu Blckknght bersama dengan beberapa informasi tambahan:

  • Sinyal Unix standarSIGPIPE dikirim ke proses penulisan ke pipa ketika tidak ada proses membaca dari pipa (lagi).

    • Ini belum tentu merupakan kondisi kesalahan ; beberapa utilitas Unix seperti head dengan desain berhenti membaca sebelum waktunya dari pipa, setelah mereka menerima cukup data.
  • Secara default - yaitu, jika proses penulisan tidak secara eksplisit menjebak SIGPIPE - proses penulisan dihentikan begitu saja , dan kode keluarnya disetel ke141 , yang dihitung sebagai 128(untuk menghentikan sinyal oleh sinyal secara umum) + 13( nomorSIGPIPE sinyal khusus ) .

  • Secara desain, bagaimanapun, Python sendiri menjebakSIGPIPE dan menerjemahkannya ke dalamIOError instance Python dengan errnonilai errno.EPIPE, sehingga skrip Python dapat menangkapnya, jika memang mau - lihat jawaban Alex L. untuk cara melakukannya.

  • Jika Python Script tidak tidak menangkapnya , Python pesan kesalahan outputIOError: [Errno 32] Broken pipe dan berakhir script dengan kode keluar1 - ini adalah gejala OP saw.

  • Dalam banyak kasus, ini lebih mengganggu daripada membantu , jadi kembali ke perilaku default diinginkan :

    • Menggunakan signalmodul memungkinkan hal itu, seperti yang dinyatakan dalam jawaban akhan ; signal.signal()mengambil sinyal untuk ditangani sebagai argumen pertama dan penangan sebagai argumen kedua; nilai penangan khusus SIG_DFLmewakili perilaku default sistem :

      from signal import signal, SIGPIPE, SIG_DFL
      signal(SIGPIPE, SIG_DFL) 
      
mklement0
sumber
32

Galat "Pipa Rusak" terjadi saat Anda mencoba untuk menulis ke pipa yang telah ditutup di ujung lainnya. Karena kode yang Anda tunjukkan tidak melibatkan pipa apa pun secara langsung, saya curiga Anda melakukan sesuatu di luar Python untuk mengarahkan output standar interpreter Python ke tempat lain. Ini bisa terjadi jika Anda menjalankan skrip seperti ini:

python foo.py | someothercommand

Masalah yang Anda hadapi someothercommandadalah keluar tanpa membaca semua yang tersedia di input standarnya. Ini menyebabkan Anda menulis (melaluiprint ) gagal di beberapa titik.

Saya dapat mereproduksi kesalahan dengan perintah berikut di sistem Linux:

python -c 'for i in range(1000): print i' | less

Jika saya menutup lesspager tanpa menggulir semua inputnya (1000 baris), Python keluar dengan yang sama seperti yang IOErrorAnda laporkan.

Blckknght
sumber
10
Ya, ini benar, tetapi bagaimana cara memperbaikinya?
JOHANNES_NYÅTT
2
tolong beri tahu saya cara memperbaikinya.
JOHANNES_NYÅTT
1
@ JOHANNES_NYÅTT: Ini dapat bekerja untuk file kecil karena buffering yang disediakan oleh pipa pada kebanyakan sistem mirip Unix. Jika Anda dapat menulis seluruh konten file ke dalam buffer, itu tidak menimbulkan kesalahan jika program lain tidak pernah membaca data itu. Namun, jika blok tulis (karena buffer penuh), maka akan gagal saat program lain berhenti. Untuk mengatakan lagi: Apa perintah lainnya? Kami tidak dapat membantu Anda lagi hanya dengan kode Python (karena itu bukan bagian yang melakukan hal yang salah).
Blckknght
2
Saya mendapat masalah ini saat melakukan piping to head ... pengecualian setelah sepuluh baris output. Cukup logis, tetapi masih tidak terduga :)
André Laszlo
4
@Blckknght: Info bagus secara umum, tetapi " perbaiki : dan" bagian yang melakukan hal yang salah ": SIGPIPEsinyal tidak selalu menunjukkan kondisi kesalahan ; beberapa utilitas Unix, terutama head, dengan desain, selama operasi normal menutup pipa lebih awal, setelah mereka membaca data sebanyak yang mereka butuhkan.
mklement0
21

Saya merasa berkewajiban untuk menunjukkan bahwa metode yang digunakan

signal(SIGPIPE, SIG_DFL) 

memang berbahaya (seperti yang sudah disarankan oleh David Bennet di komentar) dan dalam kasus saya menyebabkan bisnis lucu yang bergantung pada platform bila digabungkan dengan multiprocessing.Manager(karena pustaka standar bergantung pada BrokenPipeError yang dimunculkan di beberapa tempat). Singkatnya, begini cara saya memperbaikinya:

Pertama, Anda perlu menangkap IOError(Python 2) atau BrokenPipeError(Python 3). Bergantung pada program Anda, Anda dapat mencoba keluar lebih awal pada saat itu atau mengabaikan pengecualian:

from errno import EPIPE

try:
    broken_pipe_exception = BrokenPipeError
except NameError:  # Python 2
    broken_pipe_exception = IOError

try:
    YOUR CODE GOES HERE
except broken_pipe_exception as exc:
    if broken_pipe_exception == IOError:
        if exc.errno != EPIPE:
            raise

Namun, ini belum cukup. Python 3 masih dapat mencetak pesan seperti ini:

Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
BrokenPipeError: [Errno 32] Broken pipe

Sayangnya menghilangkan pesan itu tidak langsung, tetapi akhirnya saya menemukan http://bugs.python.org/issue11380 di mana Robert Collins menyarankan solusi ini yang saya ubah menjadi dekorator yang dapat Anda bungkus dengan fungsi utama Anda (ya, itu gila lekukan):

from functools import wraps
from sys import exit, stderr, stdout
from traceback import print_exc


def suppress_broken_pipe_msg(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except SystemExit:
            raise
        except:
            print_exc()
            exit(1)
        finally:
            try:
                stdout.flush()
            finally:
                try:
                    stdout.close()
                finally:
                    try:
                        stderr.flush()
                    finally:
                        stderr.close()
    return wrapper


@suppress_broken_pipe_msg
def main():
    YOUR CODE GOES HERE
trehn
sumber
2
Ini sepertinya tidak memperbaikinya untuk saya.
Kyle Bridenstine
Ini berfungsi untuk saya setelah saya menambahkan kecuali BrokenPipeError: lulus dalam fungsi supress_broken_pipe_msg
Rupen B
2

Saya tahu ini bukan cara yang "tepat" untuk melakukannya, tetapi jika Anda hanya tertarik untuk menghilangkan pesan kesalahan, Anda dapat mencoba solusi berikut:

python your_python_code.py 2> /dev/null | other_command
ssanch
sumber
2

Jawaban teratas ( if e.errno == errno.EPIPE:) di sini tidak benar-benar berhasil untuk saya. Saya mendapatkan:

AttributeError: 'BrokenPipeError' object has no attribute 'EPIPE'

Namun, ini harus berfungsi jika yang Anda pedulikan hanyalah mengabaikan pipa yang rusak pada penulisan tertentu. Saya pikir ini lebih aman daripada menjebak SIGPIPE:

try:
    # writing, flushing, whatever goes here
except BrokenPipeError:
    exit( 0 )

Anda jelas harus membuat keputusan, apakah kode Anda benar-benar selesai jika Anda terkena pipa yang rusak, tetapi untuk sebagian besar tujuan saya pikir itu biasanya benar. (Jangan lupa untuk menutup pegangan file, dll.)

JD Baldwin
sumber
1

Ini juga dapat terjadi jika akhir baca dari keluaran dari skrip Anda mati sebelum waktunya

yaitu open.py | otherCommand

jika otherCommand keluar dan open.py mencoba menulis ke stdout

Saya memiliki skrip melongo yang buruk yang membuat saya senang.

lkreinitz.dll
sumber
2
Ini bukan tentang proses membaca dari pipa yang sekarat , yang perlu: beberapa utilitas Unix, terutama head, berdasarkan desain, selama operasi normal menutup pipa lebih awal, setelah mereka membaca data sebanyak yang mereka butuhkan. Kebanyakan CLI hanya tunduk pada sistem untuk perilaku defaultnya: secara diam-diam menghentikan proses membaca dan melaporkan kode keluar 141(yang, dalam shell, tidak langsung terlihat, karena perintah terakhir pipeline menentukan kode keluar keseluruhan). Perilaku default Python , sayangnya, adalah mati dengan berisik .
mklement0
-2

Penutupan harus dilakukan dalam urutan terbalik dari pembukaan.

Paul
sumber
4
Meskipun itu adalah praktik yang baik secara umum, tidak melakukan itu sendiri bukanlah masalah dan tidak menjelaskan gejala OP.
mklement0