IOError: [Errno 32] Đường ống bị hỏng: Python


88

Tôi có một tập lệnh Python 3 rất đơn giản:

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()

Nhưng nó luôn nói:

IOError: [Errno 32] Broken pipe

Tôi đã xem trên internet tất cả các cách phức tạp để sửa lỗi này, nhưng tôi đã sao chép trực tiếp mã này, vì vậy tôi nghĩ rằng có điều gì đó sai với mã chứ không phải SIGPIPE của Python.

Tôi đang chuyển hướng đầu ra, vì vậy nếu tập lệnh trên được đặt tên là "open.py", thì lệnh chạy của tôi sẽ là:

open.py | othercommand

@squiguy dòng 2:print(f1.readlines())
JOHANNES_NYÅTT

2
Bạn có hai hoạt động IO xảy ra trên dòng 2: đọc từ a.txtvà ghi vào stdout. Có lẽ hãy thử chia chúng thành các dòng riêng biệt để bạn có thể xem thao tác nào kích hoạt ngoại lệ. Nếu stdoutlà một đường ống và đầu đọc đã bị đóng, thì điều đó có thể giải thích cho EPIPElỗi.
James Henstridge

1
Tôi có thể tạo lại lỗi này trên đầu ra (với các điều kiện thích hợp), vì vậy tôi nghi ngờ printcuộc gọi là thủ phạm. @ JOHANNES_NYÅTT, bạn có thể làm rõ cách bạn đang khởi chạy tập lệnh Python của mình không? Bạn đang chuyển hướng đầu ra tiêu chuẩn ở đâu đó?
Blckknght

2
Đây là một bản sao có thể có của những câu dưới đây: stackoverflow.com/questions/11423225/...

Câu trả lời:


45

Tôi đã không tái tạo vấn đề, nhưng có lẽ phương pháp này sẽ giải quyết nó: (viết từng dòng stdoutthay vì sử dụng print)

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

Bạn có thể bắt được đường ống bị hỏng? Thao tác này ghi tệp thành stdouttừng dòng cho đến khi đóng đường ống.

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

Bạn cũng cần đảm bảo rằng nó othercommandđang đọc từ đường ống trước khi nó quá lớn - /unix/11946/how-big-is-the-pipe-buffer


7
Mặc dù đây là một phương pháp lập trình tốt, nhưng tôi không nghĩ nó có liên quan gì đến lỗi đường ống bị hỏng mà người hỏi đang gặp phải (có thể liên quan đến printcuộc gọi, không phải với việc đọc tệp).
Blckknght

@Blckknght Tôi đã thêm một số câu hỏi và phương pháp thay thế và hy vọng một số phản hồi từ tác giả. Nếu sự cố đang gửi một lượng lớn dữ liệu từ một tệp đang mở trực tiếp tới câu lệnh in thì có lẽ một trong những giải pháp thay thế ở trên sẽ khắc phục được sự cố.
Alex L

(Các giải pháp đơn giản nhất thường là tốt nhất - trừ khi có một lý do cụ thể để tải toàn bộ một tập tin sau đó in nó, làm nó một cách khác nhau)
Alex L

1
Công việc tuyệt vời trong việc khắc phục sự cố này! Mặc dù tôi có thể coi câu trả lời này là đương nhiên, nhưng tôi chỉ có thể đánh giá cao điều này sau khi thấy các câu trả lời khác (và cách tiếp cận của riêng tôi) nhạt như thế nào so với câu trả lời của bạn.
Jesvin Jose

117

Vấn đề là do xử lý SIGPIPE. Bạn có thể giải quyết vấn đề này bằng cách sử dụng mã sau:

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

Xem ở đây để biết thông tin cơ bản về giải pháp này. Câu trả lời tốt hơn ở đây .


13
Điều này rất nguy hiểm, như tôi vừa phát hiện ra, bởi vì nếu bạn nhận được một SIGPIPE trên một socket (httplib hoặc bất cứ thứ gì), chương trình của bạn sẽ thoát ra mà không có cảnh báo hoặc lỗi.
David Bennett

1
@DavidBennett, tôi chắc chắn rằng ứng dụng của nó phụ thuộc vào mục đích của bạn và câu trả lời được chấp nhận là đúng. Có rất nhiều câu hỏi và đáp kỹ lưỡng ở đây để mọi người xem qua và đưa ra quyết định sáng suốt. IMO, đối với các công cụ dòng lệnh, có lẽ tốt nhất nên bỏ qua tín hiệu đường ống trong hầu hết các trường hợp.
akhan

1
Bất kỳ cách nào để làm điều này chỉ tạm thời?
Nate Glenn

2
@NateGlenn Bạn có thể lưu trình xử lý hiện có và khôi phục nó sau.
akhan

3
Ai đó có thể trả lời cho tôi lý do tại sao mọi người lại coi một bài báo blogspot là nguồn chân lý tốt hơn là tài liệu chính thức (gợi ý: mở liên kết để xem cách sửa lỗi đường ống bị hỏng đúng cách)? :)
Yurii Rabeshko

92

Để mang lại câu trả lời Alex L. của hữu ích , câu trả lời hữu ích akhan của , và câu trả lời hữu ích Blckknght của cùng với một số thông tin bổ sung:

  • Tín hiệu Unix tiêu chuẩnSIGPIPE được gửi đến một quá trình ghi vào đường ống khi không có quá trình đọc từ đường ống (nữa).

    • Đây không nhất thiết là một điều kiện lỗi ; một số tiện ích Unix chẳng hạn như head theo thiết kế ngừng đọc sớm từ đường ống, khi chúng đã nhận đủ dữ liệu.
  • Theo mặc định - tức là, nếu quá trình viết không rõ ràng cái bẫy SIGPIPE - quá trình viết chỉ đơn giản là chấm dứt , và mã lối ra của nó được thiết lập để141 , được tính như sau 128(để chấm dứt tín hiệu bằng tín hiệu nói chung) + 13( SIGPIPEtín hiệu cụ thể của số ) .

  • Tuy nhiên, theo thiết kế, Python tự bẫySIGPIPEdịch nó thành một phiên bản PythonIOErrorerrnogiá trị errno.EPIPE, để một tập lệnh Python có thể bắt nó, nếu nó chọn - hãy xem câu trả lời của Alex L. để biết cách thực hiện điều đó.

  • Nếu một Python script nào không bắt nó , Python thông báo lỗi đầu raIOError: [Errno 32] Broken pipekết thúc kịch bản với mã lối ra1 - đây là triệu chứng OP cưa.

  • Trong nhiều trường hợp, điều này gây gián đoạn hơn là hữu ích , vì vậy mong muốn hoàn nguyên về hành vi mặc định :

    • Sử dụng signalmô-đun chỉ cho phép điều đó, như đã nêu trong câu trả lời của akhan ; signal.signal()nhận một tín hiệu để xử lý làm đối số thứ nhất và một trình xử lý là đối số thứ 2; giá trị trình xử lý đặc biệt SIG_DFLđại diện cho hành vi mặc định của hệ thống :

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

32

Lỗi "Broken Pipe" xảy ra khi bạn cố gắng ghi vào một đường ống đã được đóng ở đầu bên kia. Vì mã bạn đã hiển thị không liên quan trực tiếp đến bất kỳ đường ống nào, tôi nghi ngờ bạn đang làm điều gì đó bên ngoài Python để chuyển hướng đầu ra tiêu chuẩn của trình thông dịch Python đến một nơi khác. Điều này có thể xảy ra nếu bạn đang chạy một tập lệnh như sau:

python foo.py | someothercommand

Vấn đề bạn gặp phải là someothercommandthoát ra mà không đọc mọi thứ có sẵn trên đầu vào chuẩn của nó. Điều này khiến cho việc ghi (qua print) của bạn bị lỗi tại một số điểm.

Tôi đã có thể tạo lại lỗi bằng lệnh sau trên hệ thống Linux:

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

Nếu tôi đóng lessmáy nhắn tin mà không cuộn qua tất cả dữ liệu đầu vào của nó (1000 dòng), Python sẽ thoát ra cùng với những IOErrorgì bạn đã báo cáo.


10
Đúng, điều này đúng, nhưng tôi phải sửa nó như thế nào?
JOHANNES_NYÅTT

2
xin vui lòng cho tôi biết làm thế nào để sửa chữa nó.
JOHANNES_NYÅTT

1
@ JOHANNES_NYÅTT: Nó có thể hoạt động với các tệp nhỏ do bộ đệm được cung cấp bởi các đường ống trên hầu hết các hệ thống giống Unix. Nếu bạn có thể ghi toàn bộ nội dung của tệp vào bộ đệm, nó không gây ra lỗi nếu chương trình khác không bao giờ đọc dữ liệu đó. Tuy nhiên, nếu các khối ghi (vì bộ đệm đầy), thì nó sẽ không thành công khi chương trình khác thoát. Để nói lại: Lệnh kia là gì? Chúng tôi không thể giúp bạn thêm chỉ với mã Python (vì nó không phải là phần đang làm sai).
Blckknght

1
Tôi gặp sự cố này khi đường ống đến đầu ... ngoại lệ sau mười dòng đầu ra. Khá hợp lý, nhưng vẫn bất ngờ :)
André Laszlo.

4
@Blckknght: Nói chung là thông tin tốt, nhưng hãy " sửa lỗi đó: và" phần đang thực hiện sai ": một SIGPIPEtín hiệu không nhất thiết chỉ ra tình trạng lỗi ; một số tiện ích Unix, đáng chú ý head, theo thiết kế, trong quá trình hoạt động bình thường, đóng đường ống sớm, một khi họ đã đọc như dữ liệu nhiều như họ cần.
mklement0

20

Tôi cảm thấy có nghĩa vụ chỉ ra rằng phương pháp sử dụng

signal(SIGPIPE, SIG_DFL) 

thực sự nguy hiểm (như đã được David Bennet đề xuất trong phần nhận xét) và trong trường hợp của tôi dẫn đến việc kinh doanh hài hước phụ thuộc vào nền tảng khi kết hợp vớimultiprocessing.Manager (vì thư viện tiêu chuẩn dựa vào BrokenPipeError đang được nâng lên ở một số nơi). Để làm cho một câu chuyện dài và đau đớn trở nên ngắn gọn, đây là cách tôi sửa nó:

Trước tiên, bạn cần bắt IOError(Python 2) hoặc BrokenPipeError(Python 3). Tùy thuộc vào chương trình của bạn, bạn có thể cố gắng thoát sớm tại thời điểm đó hoặc chỉ bỏ qua ngoại lệ:

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

Tuy nhiên, điều này vẫn chưa đủ. Python 3 vẫn có thể in một thông báo như thế này:

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

Thật không may, việc loại bỏ thông báo đó không đơn giản, nhưng cuối cùng tôi đã tìm thấy http://bugs.python.org/issue11380 nơi Robert Collins đề xuất giải pháp này mà tôi đã biến thành một người trang trí mà bạn có thể kết hợp chức năng chính của mình (vâng, đó là một điều điên rồ thụt đầu dòng):

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

2
Điều này dường như không khắc phục được nó cho tôi.
Kyle Bridenstine

Nó hoạt động với tôi sau khi tôi thêm ngoại trừ BrokenPipeError: vượt qua trong hàm
supress_broken_pipe_msg

2

Tôi biết đây không phải là cách "thích hợp" để làm điều đó, nhưng nếu bạn chỉ quan tâm đến việc loại bỏ thông báo lỗi, bạn có thể thử cách giải quyết này:

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

2

Câu trả lời hàng đầu ( if e.errno == errno.EPIPE:) ở đây không thực sự phù hợp với tôi. Tôi đã nhận:

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

Tuy nhiên, điều này sẽ hoạt động nếu tất cả những gì bạn quan tâm là bỏ qua các đường ống bị hỏng trên các ghi cụ thể. Tôi nghĩ nó an toàn hơn là bẫy SIGPIPE:

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

Rõ ràng là bạn phải đưa ra quyết định liệu mã của bạn có thực sự được thực hiện hay không nếu bạn chạm vào đường ống bị hỏng, nhưng đối với hầu hết các mục đích, tôi nghĩ điều đó thường đúng. (Đừng quên đóng các tay cầm tệp, v.v.)


1

Điều này cũng có thể xảy ra nếu kết thúc đọc của đầu ra từ tập lệnh của bạn chết sớm

tức là open.py | otherCommand

nếu otherCommand thoát và open.py cố gắng ghi vào stdout

Tôi đã có một kịch bản gawk tồi tệ mà đã làm điều này đáng yêu với tôi.


2
Nó không phải về quá trình đọc từ ống chết , nhất thiết: một số tiện ích Unix, đáng chú ý head, bởi thiết kế, quá trình hoạt động bình thường đóng ống sớm, một khi họ đã đọc như dữ liệu nhiều như họ cần. Hầu hết các CLI chỉ đơn giản là trì hoãn hệ thống vì hành vi mặc định của nó: lặng lẽ kết thúc quá trình đọc và báo cáo mã thoát 141(mà trong một trình bao, không rõ ràng, bởi vì lệnh cuối cùng của đường ống xác định mã thoát tổng thể). Thật không may, hành vi mặc định của Python là gây ồn ào .
mklement0

-1

Các lần đóng nên được thực hiện theo thứ tự ngược lại với các lần mở.


4
Mặc dù điều đó là thực hành tốt nói chung, nhưng không thực hiện không phải là một vấn đề và không giải thích các triệu chứng của OP.
mklement0
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.