IOError: [Errno 32] Kırık boru: Python


91

Çok basit bir Python 3 betiğim var:

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

Ama her zaman şöyle der:

IOError: [Errno 32] Broken pipe

İnternette bunu düzeltmenin tüm karmaşık yollarını gördüm, ancak bu kodu doğrudan kopyaladım, bu yüzden Python'un SIGPIPE'inde değil, kodda bir sorun olduğunu düşünüyorum.

Çıktıyı yeniden yönlendiriyorum, bu nedenle yukarıdaki komut dosyası "open.py" olarak adlandırılmışsa, çalıştırma komutum şöyle olacaktır:

open.py | othercommand

@squiguy 2. satır:print(f1.readlines())
JOHANNES_NYÅTT

2
2. satırda gerçekleşen iki IO işleminiz var: bir okuma a.txtve bir yazma stdout. Hangi işlemin istisnayı tetiklediğini görebilmek için bunları ayrı satırlara bölmeyi deneyebilirsiniz. Eğer stdoutbir boru olup okuma ucu kapatılmış, daha sonra bunun için sorumlu olabilir EPIPEhata.
James Henstridge

1
Bu hatayı çıktıda yeniden oluşturabilirim (doğru koşullar verildiğinde), bu yüzden printaramanın suçlu olduğundan şüpheleniyorum . @ JOHANNES_NYÅTT, Python betiğinizi nasıl başlattığınızı açıklayabilir misiniz? Standart çıktıyı bir yere mi yönlendiriyorsunuz?
Blckknght

2
Bu, aşağıdaki sorunun olası bir kopyasıdır: stackoverflow.com/questions/11423225/…

Yanıtlar:


48

Sorunu yeniden oluşturmadım, ancak belki bu yöntem onu ​​çözebilir: ( stdoutkullanmak yerine satır satır yazmak print)

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

Kırık boruyu yakalayabilir misin? Bu stdout, boru kapanana kadar dosyayı satır satır yazar .

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

Ayrıca othercommand, çok büyük olmadan önce borudan okuduğundan emin olmalısınız - /unix/11946/how-big-is-the-pipe-buffer


7
Bu iyi bir programlama uygulaması olsa da, soruyu soran kişinin aldığı kırık boru hatasıyla bir ilgisi olduğunu sanmıyorum (bu muhtemelen printdosyaları okumakla değil , çağrı ile ilgilidir).
Blckknght

@Blckknght Birkaç soru ve alternatif yöntemler ekledim ve yazardan bazı geri bildirimler almayı umuyordum. Sorun, açık bir dosyadan doğrudan print ifadesine büyük miktarda veri gönderiyorsa, o zaman belki yukarıdaki alternatiflerden biri sorunu çözebilir.
Alex L

(En basit çözümler genellikle en iyisidir - bütün bir dosyayı yüklemek ve sonra yazdırmak için belirli bir neden olmadıkça, farklı bir şekilde yapın)
Alex L

1
Bu sorunu gidermek için harika iş çıkardınız! Bu cevabı hafife alabilsem de, bunu ancak diğer cevapların (ve kendi yaklaşımımın) cevabınıza kıyasla nasıl soluklaştığını gördükten sonra takdir edebilirim.
Jesvin Jose

117

Sorun, SIGPIPE işleminden kaynaklanmaktadır. Aşağıdaki kodu kullanarak bu sorunu çözebilirsiniz:

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

Bu çözümle ilgili arka plan için buraya bakın . Burada daha iyi cevap .


14
Bu, az önce keşfettiğim gibi çok tehlikeli, çünkü bir soket üzerinde bir SIGPIPE alırsanız (httplib veya her neyse), programınız herhangi bir uyarı veya hata yapmadan çıkacaktır.
David Bennett

1
@DavidBennett, uygulamasına bağlı olduğundan eminim ve sizin amaçlarınız için kabul edilen cevap doğru cevaptır. Burada insanların geçmesi ve bilinçli bir karar vermesi için çok kapsamlı bir Soru-Cevap var . IMO, komut satırı araçları için, çoğu durumda boru sinyalini yok saymak muhtemelen en iyisidir.
akhan

1
Bunu geçici olarak yapmanın bir yolu var mı?
Nate Glenn

2
@NateGlenn Mevcut işleyiciyi kaydedebilir ve daha sonra geri yükleyebilirsiniz.
akhan

3
Birisi bana neden insanların bir blogspot makalesini resmi belgelerden daha iyi bir doğruluk kaynağı olarak gördüklerini yanıtlayabilir mi (ipucu: bozuk boru hatasını düzgün bir şekilde nasıl düzelteceğinizi görmek için bağlantıyı açın)? :)
Yurii Rabeshko

93

Getirmek Alex L. adlı faydalı bir yanıt , Akhan en faydalı bir yanıt ve Blckknght en faydalı bir yanıt bazı ek bilgilerle birlikte:

  • Standart Unix sinyaliSIGPIPE , borudan işlem okuması olmadığında (artık) bir boruya yazan bir işleme gönderilir .

    • Bu mutlaka bir hata koşulu değildir; head tasarım gereği bazı Unix araçları , yeterli veri aldıktan sonra bir borudan erken okumayı durdurur.
  • Varsayılan olarak - yani, yazma işlemi açık bir şekilde yakalanmazsa SIGPIPE - yazma işlemi basitçe sonlandırılır ve çıkış kodu şu141 şekilde hesaplanır 128(genel olarak sinyalle sonlandırmayı işaret etmek için) + 13( SIGPIPE's özel sinyal numarası ) .

  • Bununla birlikte, tasarım gereği, Python'un kendisiSIGPIPE onu IOErroryakalar ve errnodeğeri olan bir Python örneğine çevirirerrno.EPIPE , böylece bir Python betiği onu seçerse yakalayabilir - bunu nasıl yapacağına dair Alex L.'nin cevabına bakın.

  • Python Eğer komut yok değil yakalamak , Python'un çıkışları hata mesajıIOError: [Errno 32] Broken pipe ve çıkış koduyla senaryoyu sonlandırır1 - Bu belirti OP testeredir.

  • Çoğu durumda, bu yardımcı olmaktan çok rahatsız edicidir , bu nedenle varsayılan davranışa geri dönmek arzu edilir :

    • signalModülü kullanmak, akhan'ın cevabında belirtildiği gibi tam da bunu sağlar ; signal.signal()1. bağımsız değişken olarak işlemek için bir sinyal ve 2. olarak bir eylemci alır; özel işleyici değeri SIG_DFL, sistemin varsayılan davranışını temsil eder :

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

32

Diğer uçta kapatılmış bir boruya yazmaya çalıştığınızda "Kırık Boru" hatası oluşur. Gösterdiğiniz kod doğrudan herhangi bir boruyu içermediğinden, Python yorumlayıcısının standart çıktısını başka bir yere yönlendirmek için Python dışında bir şey yaptığınızı sanıyorum. Böyle bir komut dosyası çalıştırıyorsanız bu olabilir:

python foo.py | someothercommand

Sahip olduğunuz sorun someothercommand, standart girişinde bulunan her şeyi okumadan çıkmaktır. Bu, yazmanızın (aracılığıyla print) bir noktada başarısız olmasına neden olur .

Bir Linux sisteminde aşağıdaki komutla hatayı yeniden oluşturabildim:

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

lessÇağrı cihazını tüm girişlerinde (1000 satır) kaydırmadan kapatırsam , Python IOErrorbildirdiğinizle çıkar .


10
Evet, bu doğru ama nasıl düzeltebilirim?
JOHANNES_NYÅTT

2
lütfen nasıl düzelteceğimi bana bildirin.
JOHANNES_NYÅTT

1
@ JOHANNES_NYÅTT: Unix benzeri sistemlerin çoğunda borular tarafından sağlanan arabelleğe alma nedeniyle küçük dosyalar için çalışabilir. Dosyaların tüm içeriğini arabelleğe yazabilirseniz, diğer program bu verileri asla okumazsa bu bir hata oluşturmaz. Ancak, yazma blokları varsa (çünkü arabellek dolu), o zaman diğer program sonlandırıldığında başarısız olur. Tekrar söylemek gerekirse: Diğer komut nedir? Artık yalnızca Python koduyla size yardımcı olamayız (çünkü yanlış şeyi yapan kısım bu değildir).
Blckknght

2
Bu problemi başa boru döşerken yaşadım ... on satır çıkıştan sonra istisna. Oldukça mantıklı, ama yine de beklenmedik :)
André Laszlo

4
@Blckknght: genel olarak iyi bilgi, ancak yeniden " düzeltme olun: ve 'yapıyor kısmı yanlış bir şey': Bir SIGPIPEsinyal mutlaka bir göstermez hata koşulu; özellikle bazı Unix hizmeti head, tasarım gereği, normal çalışma sırasında yakın boru erken, onlar çok veri olarak okudum kez olarak onlar gerekli.
mklement0

21

Kullanan yöntemin olduğunu belirtmek zorunda hissediyorum

signal(SIGPIPE, SIG_DFL) 

gerçekten tehlikelidir (yorumlarda David Bennet tarafından daha önce önerildiği gibi) ve benim multiprocessing.Managerdurumumla birleştirildiğinde platforma bağlı komik işlere yol açtı (çünkü standart kütüphane, BrokenPipeError'ın birkaç yerde yükseltilmesine dayanıyor). Uzun ve acı verici bir hikayeyi kısaltmak için, bunu şu şekilde düzelttim:

Öncelikle IOError(Python 2) veya BrokenPipeError(Python 3) ' ü yakalamalısın . Programınıza bağlı olarak, bu noktada erken çıkmayı deneyebilir veya istisnayı göz ardı edebilirsiniz:

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

Ancak bu yeterli değil. Python 3 yine de böyle bir mesaj yazdırabilir:

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

Maalesef bu mesajdan kurtulmak kolay değil, ancak sonunda http://bugs.python.org/issue11380 Robert Collins'in ana işlevinizi sarabileceğiniz bir dekoratöre dönüştürdüğüm bu geçici çözümü önerdiği yerde buldum (evet, bu biraz çılgınca girinti):

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
Bu benim için düzeltmedi.
Kyle Bridenstine

BrokenPipeError dışında ekledikten sonra benim için çalıştı: supress_broken_pipe_msg işlevini geçirin
Rupen B

2

Bunun "doğru" bir yol olmadığını biliyorum, ancak yalnızca hata mesajından kurtulmakla ilgileniyorsanız, şu geçici çözümü deneyebilirsiniz:

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

2

Buradaki en iyi cevap ( if e.errno == errno.EPIPE:) benim için pek işe yaramadı. Bende var:

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

Ancak, tek umursadığınız belirli yazılarda kırık boruları görmezden gelmekse, bu işe yaramalıdır. SIGPIPE'i tuzağa düşürmekten daha güvenli olduğunu düşünüyorum:

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

Açıkçası, kodunuzun gerçekten, gerçekten, eğer kırık boruya çarparsanız yapılırsa, bir karar vermelisiniz, ancak çoğu amaç için bunun genellikle doğru olacağını düşünüyorum. (Dosya tanıtıcılarını vb. Kapatmayı unutmayın.)


1

Bu, betiğinizden çıktının okunan ucu zamanından önce ölürse de oluşabilir.

yani open.py | diğerCommand

otherCommand çıkarsa ve open.py stdout'a yazmaya çalışırsa

Bana bu kadar güzel gelen kötü bir bakış açım senaryom vardı.


2
Bu süreç borusundan okuma hakkında değil ölmekte mutlaka: Bazı Unix hizmeti özellikle head, tasarım gereği, normal çalışma sırasında çok daha veri olarak okudum kez yakın boru erken olarak onlar gerekli. Çoğu CLI, basitçe varsayılan davranışı için sistemi erteler: okuma sürecini sessizce sonlandırır ve çıkış kodunu bildirir 141(ki bu, bir kabukta, bir boru hattının son komutu genel çıkış kodunu belirlediğinden, kolayca görünmez ). Python'un varsayılan davranışı ne yazık ki gürültüyle ölmektir .
mklement0

-2

Kapatma, açılış sırasının tersine göre yapılmalıdır.


4
Bu genel olarak iyi bir uygulama olsa da, yapmamak kendi başına bir sorun değildir ve OP'nin semptomlarını açıklamaz.
mklement 0
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.