Python C program subprocess hangs at "for line in iter"
Программный подпроцесс Python C зависает на "для строки в iter"
Хорошо, итак, я пытаюсь запустить программу на C из скрипта на Python. В настоящее время я использую тестовую программу на C:
#include<stdio.h>
intmain(){ while (1) { printf("2000\n"); sleep(1); } return0; }
Для имитации программы, которую я буду использовать, которая постоянно снимает показания с датчика. Затем я пытаюсь прочитать выходные данные (в данном случае "2000") из программы C с помощью подпроцесса на python:
#!usr/bin/python import subprocess
process = subprocess.Popen("./main", stdout=subprocess.PIPE) whileTrue: for line initer(process.stdout.readline, ''): print line,
но это не работает. При использовании инструкций print он запускает .Popen строку, затем ожидает на for line in iter(process.stdout.readline, ''):, пока я не нажму Ctrl-C.
Почему это? Это именно то, что большинство примеров, которые я видел, имеют в качестве кода, и все же он не считывает файл.
Есть ли способ заставить его запускаться только тогда, когда есть что читать?
Исправьте буфер стандартного вывода непосредственно в программе C
stdioПрограммы на основе Python C, как правило, буферизуются по строкам, если они выполняются интерактивно в терминале, и буферизуются по блокам, когда их стандартный вывод перенаправляется в канал. В последнем случае вы не увидите новых строк до тех пор, пока буфер не переполнится или не будет сброшен.
Чтобы избежать вызова fflush() после каждого printf() вызова, вы могли бы принудительно выводить данные с буферизацией строк, вызвав программу на C в самом начале:
setvbuf(stdout, (char *) NULL, _IOLBF, 0); /* make line buffered stdout */
Как только печатается новая строка, в этом случае буфер очищается.
Или исправьте это без изменения исходного кода программы на C
Существует stdbuf утилита, которая позволяет изменять тип буферизации без изменения исходного кода, например:
from subprocess import Popen, PIPE
process = Popen(["stdbuf", "-oL", "./main"], stdout=PIPE, bufsize=1) forlinein iter(process.stdout.readline, b''): print line, process.communicate() # close process' stream, waitfor it toexit
Чтобы заставить подпроцесс думать, что он выполняется в интерактивном режиме, вы могли бы использовать pexpect module или его аналоги, примеры кода, использующие pexpect и pty модули, см. в Python subprocess readlines() зависает. Вот вариант pty приведенного там примера (он должен работать в Linux):
#!/usr/bin/env python import os import pty import sys from select import select from subprocess import Popen, STDOUT
master_fd, slave_fd = pty.openpty() # provide tty to enable line buffering process = Popen("./main", stdin=slave_fd, stdout=slave_fd, stderr=STDOUT, bufsize=0, close_fds=True) timeout = .1# ugly but otherwise `select` blocks on process' exit # code is similar to _copy() from pty.py with os.fdopen(master_fd, 'r+b', 0) as master: input_fds = [master, sys.stdin] whileTrue: fds = select(input_fds, [], [], timeout)[0] if master in fds: # subprocess' output is ready data = os.read(master_fd, 512) # <-- doesn't block, may return less ifnot data: # EOF input_fds.remove(master) else: os.write(sys.stdout.fileno(), data) # copy to our stdout if sys.stdin in fds: # got user input data = os.read(sys.stdin.fileno(), 512) ifnot data: input_fds.remove(sys.stdin) else: master.write(data) # copy it to subprocess' stdin ifnot fds: # timeout in select() if process.poll() isnotNone: # subprocess ended # and no output is buffered <-- timeout + dead subprocess assertnot select([master], [], [], 0)[0] # race is possible os.close(slave_fd) # subproces don't need it anymore break rc = process.wait() print("subprocess exited with status %d" % rc)
Your program isn't hung, it just runs very slowly. Your program is using buffered output; the "2000\n" data is not being written to stdout immediately, but will eventually make it. In your case, it might take BUFSIZ/strlen("2000\n") seconds (probably 1638 seconds) to complete.