Python Popen, закрытие потоков и нескольких процессов

StackOverflow https://stackoverflow.com/questions/617308

  •  03-07-2019
  •  | 
  •  

Вопрос

У меня есть некоторые данные, которые я хотел бы сжать, uuencode, а затем распечатать в стандартный формат.В основном у меня есть:

compressor = Popen("gzip", stdin = subprocess.PIPE, stdout = subprocess.PIPE)
encoder    = Popen(["uuencode", "dummy"], stdin = compressor.stdout)

Я передаю данные в компрессор через компрессор.stdin.write(stuff).

Что мне действительно нужно сделать, так это отправить EOF компрессору, и я понятия не имею, как это сделать.

В какой-то момент я попробовал компрессор.stdin.close(), но это не сработало — он работает хорошо, когда компрессор записывает файл напрямую, но в приведенном выше случае процесс не завершается и останавливается на компрессоре. ждать().

Предложения?В данном случае примером является gzip, и мне действительно нужно что-то сделать с передачей вывода одного процесса в другой.

Примечание:Данные, которые мне нужно сжать, не помещаются в памяти, поэтому общение здесь не очень хороший вариант.Кроме того, если я просто забегу

compressor.communicate("Testing") 

после двух строк выше все равно зависает с ошибкой

  File "/usr/lib/python2.4/subprocess.py", line 1041, in communicate
    rlist, wlist, xlist = select.select(read_set, write_set, [])
Это было полезно?

Решение

Я подозреваю, что проблема в порядке открытия труб.UUEncode забавен тем, что он будет скулить, когда вы его запускаете, если нет входящего канала правильным способом (попробуйте запустить эту чертову штуку самостоятельно в вызове Popen, чтобы увидеть взрыв, используя только PIPE в качестве стандартного ввода и стандартного вывода)

Попробуй это:

encoder = Popen(["uuencode", "dummy"], stdin=PIPE, stdout=PIPE)
compressor = Popen("gzip", stdin=PIPE, stdout=encoder.stdin)

compressor.communicate("UUencode me please")
encoded_text = encoder.communicate()[0]
print encoded_text

begin 644 dummy
F'XL(`%]^L$D``PL-3<U+SD])5<A-52C(24TL3@4`;2O+"!(`````
`
end

Вы правы, кстати...нет способа отправить общий EOF по каналу.В конце концов, каждая программа действительно определяет свой собственный EOF.Чтобы это сделать, нужно закрыть трубу, как вы пытались это сделать.

РЕДАКТИРОВАТЬ:Мне нужно уточнить вопрос о uuencode.Как программа-оболочка, ее поведение по умолчанию — ожидать ввода с консоли.Если вы запустите его без «живого» входящего канала, он заблокирует ожидание ввода с консоли.Если открыть кодировщик за секунду до того, как вы отправите материал в трубу компрессора, кодировщик заблокируется, ожидая, пока вы начнете печатать.Джеруб был прав в том, что что-то мешало.

Другие советы

Это не то, что вам следует делать непосредственно в Python, есть некоторые особенности в том, как все работает, поэтому гораздо лучше делать это с помощью оболочки.Если вы можете просто использовать subprocess.Popen("foo | bar",shell=True), то тем лучше.

Возможно, происходит следующее: gzip еще не смог вывести все свои входные данные, и процесс не выйдет из строя до тех пор, пока не будут завершены записи на стандартный вывод.

Вы можете посмотреть, какой системный вызов блокируется процессом, если используете strace.Использовать ps auxwf чтобы узнать, какой процесс является процессом gzip, затем используйте strace -p $pidnum чтобы увидеть, какой системный вызов он выполняет.Обратите внимание, что stdin — это FD 0, а stdout — FD 1, вы, вероятно, увидите, что он читает или записывает эти файловые дескрипторы.

если вы просто хотите сжать файлы и вам не нужны оболочки файлов, рассмотрите возможность использования модуля zlib

import zlib
compressed = zlib.compress("text")

есть ли причина, по которой предложения каналов Shell=True и unix не работают?

from subprocess import *

pipes = Popen("gzip | uuencode dummy", stdin=PIPE, stdout=PIPE, shell=True)
for i in range(1, 100):
    pipes.stdin.write("some data")
pipes.stdin.close()
print pipes.stdout.read()

кажется, работает

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top