Программный подпроцесс Python C зависает на "для строки в iter"
Хорошо, итак, я пытаюсь запустить программу на C из скрипта на Python. В настоящее время я использую тестовую программу на C:
#include <stdio.h>
int main() {
while (1) {
printf("2000\n");
sleep(1);
}
return 0;
}
Для имитации программы, которую я буду использовать, которая постоянно снимает показания с датчика.
Затем я пытаюсь прочитать выходные данные (в данном случае "2000"
) из программы C с помощью подпроцесса на python:
#!usr/bin/python
import subprocess
process = subprocess.Popen("./main", stdout=subprocess.PIPE)
while True:
for line in iter(process.stdout.readline, ''):
print line,
но это не работает. При использовании инструкций print он запускает .Popen
строку, затем ожидает на for line in iter(process.stdout.readline, ''):
, пока я не нажму Ctrl-C.
Почему это? Это именно то, что большинство примеров, которые я видел, имеют в качестве кода, и все же он не считывает файл.
Есть ли способ заставить его запускаться только тогда, когда есть что читать?
Переведено автоматически
Ответ 1
Это проблема с буферизацией блоков.
Ниже приводится расширенная для вашего случая версия моего ответа на вопрос Python: чтение потоковых входных данных из subprocess.communicate().
Исправьте буфер стандартного вывода непосредственно в программе 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)
for line in iter(process.stdout.readline, b''):
print line,
process.communicate() # close process' stream, wait for it to exit
Доступны и другие утилиты, см. Отключить буферизацию в канале.
Или используйте псевдо-TTY
Чтобы заставить подпроцесс думать, что он выполняется в интерактивном режиме, вы могли бы использовать 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]
while True:
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
if not 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)
if not data:
input_fds.remove(sys.stdin)
else:
master.write(data) # copy it to subprocess' stdin
if not fds: # timeout in select()
if process.poll() is not None: # subprocess ended
# and no output is buffered <-- timeout + dead subprocess
assert not 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)
Or use pty
via pexpect
pexpect
wraps pty
handling into higher level interface:
#!/usr/bin/env python
import pexpect
child = pexpect.spawn("/.main")
for line in child:
print line,
child.close()
Q: Why not just use a pipe (popen())? explains why pseudo-TTY is useful.
Ответ 2
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.
After this line:
printf("2000\n");
add
fflush(stdout);
Ответ 3
See readline docs.
Your code:
process.stdout.readline
Is waiting for EOF or a newline.
I cannot tell what you are ultimately trying to do, but adding a newline to your printf, e.g., printf("2000\n");
, should at least get you started.