Подтвердить что ты не робот

Новичок подпроцесс python: "write error: Broken pipe"

Благодаря полезным предложениям ниже:

Итак, это кажется фиксированным, когда I

  • отдельные команды для индивидуальных вызовов в Popen
  • 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)

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

Я слишком долго пытался решить проблему с помощью простого подпроцесса. 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

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

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

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

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

4b9b3361

Ответ 1

Из рецептов на 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

Это связано с тем, что вы не должны использовать "shell-трубы" в команде, переданной в 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()

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

Ответ 3

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

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

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

например, вместо "head -1" (в моем случае я только хотел первой строки), я сделал awk 'NR == 1'

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

Ответ 4

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

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

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

Ответ 5

попробуйте обмениваться(), вместо того, чтобы напрямую читать из stdout.

python docs говорят это:

"Предупреждение Используйте функцию связи(), а не .stdin.write,.stdout.read или .stderr.read, чтобы избежать взаимоблокировок из-за любой из других буферов операционной системы заполнение и блокировка ребенка Процесс".

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