2010-11-05 2 views
12

Благодаря полезным предложения ниже:новичка питон подпроцесс: «Ошибка записи: Broken труба»

Так что, кажется, фиксируется, когда я

  1. отдельных команд в индивидуальные вызовы POPEN
  2. STDERR = subprocess.PIPE в качестве аргумента для каждой цепи Popen.

Новый код:

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) 

Оригинал сообщение:

Я потратил слишком много времени, пытаясь решить проблему с пылом простого subprocess.Popen.

Код:

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

Выход для файла ~ 1000 строк длиной:

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

Выход для файлов> 241 строк в длину:

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

Выход для файла < 241 линии в порядке.

Я читал документы и поисковые запросы, как сумасшедшие, но есть что-то принципиальное в модуле подпроцесса, который мне не хватает ... возможно, с буферами. Я пробовал p.stdout.flush() и играл с размером буфера и p.wait(). Я попытался воспроизвести это с помощью команд типа sleep 20; cat averagefile ', но это, кажется, работает без ошибок.

+0

... и p2.communic() также работает, но я думаю, что это может вызвать проблемы, если выход большой. – mathtick

+1

«Новый код» очень полезен. Любовь, что я могу использовать ту же самую командную команду, которую я использовал при тестировании в оболочке. Два предложения: 1) сделать множественное число: run_shell_commands 2) либо удалить, прокомментировать, либо добавить debug = false в операторы печати внутри функции – PeterVermont

+1

Спасибо. Иди в том же сломанном трубке с файлами определенного размера. Используется ваш код, и он работает как шарм. – poof

ответ

10

Из рецептов на subprocess документы:

# 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

Я думал, что общения следует избегать для большого выхода? – mathtick

+1

Оболочка не вызывала проблемы, но почему-то расщепление команд в «правильном» месте, похоже, исправить. Благодаря! – mathtick

+0

@mathtick: вы действительно должны перебирать PIPE вместо того, чтобы приписывать большой вывод некоторому экземпляру строки, иначе вы рискуете исключением из памяти. –

0

Вам не нужно shell=True. Не вызывайте оболочку. Вот как я это сделаю:

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

Посмотрите, есть ли у вас проблемы с буфером после использования?

+0

Скорлупа, похоже, не вызывала проблемы. Разделение команд в нужном месте, похоже, исправлено (см. Обновление). Благодаря! – mathtick

4

Это потому, что вы не должны использовать «трубы оболочки» в команде передается subprocess.Popen, вы должны использовать subprocess.PIPE так:

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

Но я должен сказать, что то, что вы пытаетесь do может быть сделано в чистом питоне, а не в вызове команд оболочки.

+3

Я приветствую 13+ миллионов строк, возвращаю 100k + линии матчей, сортируя, разрезая и беря «голову». Это занимает несколько секунд в оболочке. Это было навсегда на питоне. Я пробовал читать(), и я думал, что я пытался разделить команды, но я думаю, что это та же проблема. Будет опубликован после тестирования больше ... – mathtick

+1

Разделение команд, похоже, исправило его, даже если я все еще использую shell = True. – mathtick

0

попробуйте использовать communicate(), вместо того, чтобы читать непосредственно со стандартного вывода.

питон документы говорят это:

"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

Я пробовал p.communicate() [0], но это не исправить проблему. Разделение команд было соответствующим образом (см. Выше). Я до сих пор не понимаю, почему это зафиксировало ситуацию. – mathtick

1

Я имел ту же ошибку.Даже поместите трубку в сценарий bash и выполним это вместо трубы в Python. Из Python он получит ошибку разбитого трубопровода, из bash этого не будет.

Мне кажется, что, возможно, последняя команда перед головой бросает ошибку, поскольку она (сортировка) STDOUT закрыта. Python должен подбирать это, тогда как с оболочкой ошибка затихает. Я изменил свой код, чтобы потреблять весь вход, и ошибка исчезла.

Имеет смысл также с меньшими файлами, работающими, поскольку труба, вероятно, буферизирует весь выход до выхода главы. Это объясняет перерывы в больших файлах.

например, вместо «голов -1» (в моем случае, я только хотел первую линию), я сделал AWK «NR == 1»

Есть, вероятно, более эффективные способы сделать это в зависимости от того, где «head -X» встречается в трубе.

Смежные вопросы