обработка непрерывного вывода команды в python

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

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

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

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

Все идет нормально. Теперь для большого вопроса …

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

 poll.register(process.stdout) 

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

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

Использование 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 является обычным 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 события select.POLLHUP . Затем вы можете вызвать метод unregister и, наконец, выйти из цикла, когда все FD будут закрыты.

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

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