Python Popen, закрытие потоков и нескольких процессов - PullRequest
2 голосов
/ 06 марта 2009

У меня есть некоторые данные, которые я хотел бы распечатать, распаковать и распечатать в стандартном формате. Что у меня в основном есть:

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

Подача данных в компрессор осуществляется с помощью compressor.stdin.write (материал).

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

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

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

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

compressor.communicate("Testing") 

после 2 строк выше, он все еще зависает с ошибкой

  File "/usr/lib/python2.4/subprocess.py", line 1041, in communicate
    rlist, wlist, xlist = select.select(read_set, write_set, [])

Ответы [ 3 ]

4 голосов
/ 06 марта 2009

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

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

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. Как оболочка, по умолчанию она ожидает консольного ввода. Если вы запустите его без «живого» входящего канала, он заблокирует ожидание ввода с консоли. Открыв кодировщик на секунду, перед тем, как вы отправили материал по трубе компрессора, кодировщик заблокировал ожидание, когда вы начнете печатать. Иеруб был прав в том, что что-то блокировало.

3 голосов
/ 06 марта 2009

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

Что может случиться, так это то, что gzip еще не смог вывести все свои входные данные, и процесс не завершит свою работу, пока его записи stdout не будут завершены.

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

1 голос
/ 06 марта 2009

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

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

есть ли причина, по которой предложения shell = True и unix pipe не будут работать?

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

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

...