2010-11-05 11 views
12

Dzięki pomocnych wskazówek poniżej:początkujących pyton podproces: „błąd zapisu: Broken pipe”

Więc wydaje się być ustalona, ​​kiedy

  1. oddzielne polecenia w poszczególnych wezwań do POPEN
  2. stderr = subprocess.PIPE jako argument dla każdego łańcucha Popen.

nowy kod:

import subprocess 
import shlex 
import logging 

def run_shell_commands(cmds): 
    """ Run commands and return output from last call to subprocess.Popen. 
     For usage see the test below. 
    """ 
    # split the commands 
    cmds = cmds.split("|") 
    cmds = list(map(shlex.split,cmds)) 

    logging.info('%s' % (cmds,)) 

    # run the commands 
    stdout_old = None 
    stderr_old = None 
    p = [] 
    for cmd in cmds: 
     logging.info('%s' % (cmd,)) 
     p.append(subprocess.Popen(cmd,stdin=stdout_old,stdout=subprocess.PIPE,stderr=subprocess.PIPE)) 
     stdout_old = p[-1].stdout 
     stderr_old = p[-1].stderr 
    return p[-1] 


pattern = '"^85567  "' 
file = "j" 

cmd1 = 'grep %s %s | sort -g -k3 | head -10 | cut -d" " -f2,3' % (pattern, file) 
p = run_shell_commands(cmd1) 
out = p.communicate() 
print(out) 

Original post:

spędziłem zbyt długo stara się rozwiązać problem orurowanie prostą subprocess.Popen.

Kod:

import subprocess 
cmd = 'cat file | sort -g -k3 | head -20 | cut -f2,3' % (pattern,file) 
p = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE) 
for line in p.stdout: 
    print(line.decode().strip()) 

wyjścia dla plików ~ 1000 linii o długości:

... 
sort: write failed: standard output: Broken pipe 
sort: write error 

wyjścia dla plików> 241 linii o długości:

... 
sort: fflush failed: standard output: Broken pipe 
sort: write error 

wyjście do pliku < 241 długość linii jest w porządku.

Przeczytałem dokumentację i szukałem czegoś jak szalonego, ale jest coś fundamentalnego w module podprocesu, którego mi brakuje ... może z buforami. Próbowałem p.stdout.flush() i grając z rozmiarem bufora i p.wait(). Próbowałem odtworzyć to za pomocą poleceń takich jak "sleep 20; cat moderatefile ", ale wydaje się, że działa bez błędu.

+0

... i p2.communicate() również działa, ale myślę, że może to spowodować problemy, jeśli wyjście jest duże. – mathtick

+1

"Nowy kod" jest bardzo pomocny. Uwielbiam, że mogę użyć dokładnie tego samego polecenia wypróbowanego, którego użyłem podczas testowania w powłoce. Dwie sugestie: 1) tworzą liczbę mnogą: run_shell_commands 2) usuń, skomentuj lub dodaj debug = false wokół instrukcji print wewnątrz funkcji – PeterVermont

+1

Dzięki. Wystąpił ten sam problem z potokami z plikami przekraczającymi pewien rozmiar. Użyłeś swojego kodu i działa jak urok. – poof

Odpowiedz

10

Od receptur na subprocess docs:

# To replace shell pipeline like output=`dmesg | grep hda` 
p1 = Popen(["dmesg"], stdout=PIPE) 
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) 
output = p2.communicate()[0] 
+2

Myślałem, że należy unikać komunikowania się z dużymi wyjściami? – mathtick

+1

Powłoka nie powodowała problemu, ale z jakiegoś powodu rozdzielenie poleceń w "właściwym" miejscu wydaje się naprawić. Dzięki! – mathtick

+0

@mathtick: rzeczywiście powinieneś iterować przez PIPE zamiast przypisywać duże wyjście do jakiejś instancji napisu, w przeciwnym razie ryzykujesz wyjątkiem braku pamięci. –

0

Nie trzeba shell=True. Nie przywołuj powłoki. Oto, jak bym to zrobił:

p = subprocess.Popen(cmd, stdout=subprocess.PIPE) 
stdout_value = p.communicate()[0] 
stdout_value # the output 

Sprawdź, czy napotkasz problem z buforem po użyciu?

+0

Powłoka nie wydaje się być przyczyną problemu. Podział poleceń we właściwym miejscu najwyraźniej naprawił go (patrz aktualizacja). Dzięki! – mathtick

4

To dlatego, że nie powinno się używać „rur Shell” w przekazanym subprocess.Popen polecenia, należy użyć subprocess.PIPE takiego:

from subprocess import Popen, PIPE 

p1 = Popen('cat file', stdout=PIPE) 
p2 = Popen('sort -g -k 3', stdin=p1.stdout, stdout=PIPE) 
p3 = Popen('head -20', stdin=p2.stdout, stdout=PIPE) 
p4 = Popen('cut -f2,3', stdin=p3.stdout) 
final_output = p4.stdout.read() 

ale muszę powiedzieć, że to, co starasz się można zrobić w czystym Pythonie, zamiast wywoływać kilka poleceń powłoki.

+3

Jestem grepping 13+ milionów linii zwraca 100k + linii meczów, sortowanie, cięcie i biorąc "głowę". To zajmuje kilka sekund w powłoce. Trwało to wiecznie w pytonie. Próbowałem read() i myślałem, że próbowałem rozdzielić polecenia, ale myślę, że to ten sam problem. Wyśle ponownie po przetestowaniu więcej ... – mathtick

+1

Dzielenie poleceń wydaje się naprawić, nawet jeśli nadal używam powłoki = prawda. – mathtick

0

spróbuj użyć communicate() zamiast czytania bezpośrednio ze stdout.

docs Python powiedzieć:

"Warning Use communicate() rather than .stdin.write, .stdout.read or .stderr.read to avoid deadlocks due to any of the other OS pipe buffers filling up and blocking the child process."

http://docs.python.org/library/subprocess.html#subprocess.Popen.stdout

p = subprocess.Popen(cmd, stdout=subprocess.PIPE) 
output = p.communicate[0] 
for line in output: 
    # do stuff 
+0

Próbowałem p.communicate() [0], ale to nie rozwiązało problemu. Rozdzielenie poleceń odpowiednio (patrz wyżej). Wciąż nie bardzo rozumiem, dlaczego to naprawiło. – mathtick

1

I zostały posiadające ten sam błąd.Nawet umieściłem potok w skrypcie basha i wykonałem go zamiast potoku w Pythonie. Od Pythona otrzyma błąd zepsutej rury, od basha to nie będzie.

Wydaje mi się, że być może ostatnia komenda przed głowicą rzuca błąd, ponieważ jest (sort) STDOUT jest zamknięty. Python musi się tym zająć, podczas gdy w powłoce błąd jest cichy. Zmieniłem kod, aby pochłonąć całe wejście i błąd zniknął.

Byłoby sensowne również w przypadku mniejszych plików, ponieważ rura prawdopodobnie buforuje całe wyjście przed wyjściem głowy. To wyjaśniałoby przerwy w większych plikach.

przykład zamiast „głowy -1” (w moim przypadku, ja tylko chce pierwszy wiersz), Zrobiłem awk „NR == 1”

Istnieje prawdopodobnie lepsze sposoby osiągnięcia tego w zależności od miejsca, w którym "głowa -X" występuje w rurze.