обработка непрерывного вывода команды в python - PullRequest
5 голосов
/ 13 января 2012

Я новичок в Python, использую Perl в течение многих лет. Типичная вещь, которую я делаю все время, это то, что perl открывает команду как канал и назначает ее вывод локальной переменной для обработки. Другими словами:

"open CMD, "$command|";
$output=<CMD>;

кусок торта. Я думаю, что я могу сделать что-то подобное в Python таким образом:

args=[command, args...]
process=subprocess.Popen(args, stdout=subprocess.PIPE)
output=process.communicate()

пока все хорошо. Теперь для большого вопроса ...

Если я запускаю эту команду, используя ssh на нескольких платформах, я могу затем отслеживать дескрипторы в perl внутри цикла выбора, чтобы обрабатывать результаты по мере их поступления. Я нашел модули выбора и опроса python, но я не совсем уверен, как их использовать. Документация говорит, что poll будет использовать дескриптор файла, но когда я пытаюсь передать переменную 'process' выше в poll.register (), я получаю сообщение об ошибке, что это должен быть int или метод fileno (). Так как Popen () использовал stdout, я попытался вызвать

poll.register(process.stdout)

и больше не выдает ошибку, а просто зависает.

Любые предложения / указатели о том, как сделать что-то подобное этой работе?

Ответы [ 2 ]

7 голосов
/ 13 января 2012

Использование select.poll: необходимо передать объекты методом fileno или дескрипторами реального файла (целыми числами) :

import os, sys, select, subprocess

args = ['sh', '-c', 'while true; do date; sleep 2; done']
p1 = subprocess.Popen(args, stdout=subprocess.PIPE)
p2 = subprocess.Popen(args, stdout=subprocess.PIPE)

while True:
    rlist, wlist, xlist = select.select([p1.stdout, p2.stdout], [], [])
    for stdout in rlist:
        sys.stdout.write(os.read(stdout.fileno(), 1024))

Вы будете видеть, что он делает паузу каждые две секунды, а затем производит больше выходных данных по мере их появления. «Хитрость» заключается в том, что p1.stdout - это обычный файловый объект с методом fileno, который возвращает номер дескриптора файла. Это все, что нужно select.

Обратите внимание, что я читаю из stdout, используя os.read вместо простого вызова stdout.read. Это связано с тем, что такой вызов, как stdout.read(1024), заставит вашу программу ждать, пока не будет прочитано запрошенное количество байтов. Меньшее количество байтов возвращается только при достижении EOF, но, поскольку EOF никогда не достигается, вызов stdout.read блокируется до тех пор, пока не будет прочитано не менее 1024 байтов.

Это не похоже на функцию os.read, которая не имеет никаких сомнений в отношении досрочного возврата, когда доступно меньшее количество байтов - она ​​сразу же возвращает то, что доступно. Другими словами, получение менее 1024 байтов от os.read(stdout.fileno(), 1024) не является признаком того, что stdout был закрыт.

Использование select.epoll практически идентично, за исключением того, что вы получаете «сырой» файловый дескриптор (FD) обратно, который вам необходим os.read, чтобы иметь возможность читать:

import os, sys, select, subprocess

args = ['sh', '-c', 'while true; do date; sleep 2; done']
p1 = subprocess.Popen(args, stdout=subprocess.PIPE)
p2 = subprocess.Popen(args, stdout=subprocess.PIPE)

poll = select.poll()
poll.register(p1.stdout)
poll.register(p2.stdout)

while True:
    rlist = poll.poll()
    for fd, event in rlist:
        sys.stdout.write(os.read(fd, 1024))

Закрытый FD сигнализируется возвращаемым событием select.POLLHUP. Затем вы можете вызвать метод unregister и, наконец, выйти из цикла, когда все FD закрыты.

Наконец, позвольте мне заметить, что вы, конечно, могли бы создать словарь с отображением файловых дескрипторов обратно на файловые объекты и, следовательно, на запущенные вами процессы.

2 голосов
/ 11 августа 2015
import subprocess

p = subprocess.Popen('apt-get autoclean', stdout=subprocess.PIPE, stderr = None, shell=True)

for line in iter(p.stdout.readline, ''):

    print line

p.stdout.flush()
p.stdout.close()

print ("Done")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...