Ok, also versuche ich, ein C-Programm aus einem Python-Skript auszuführen. Derzeit verwende ich ein Test-C-Programm:
#include <stdio.h>
int main() {
while (1) {
printf("2000\n");
sleep(1);
}
return 0;
}
Um das Programm zu simulieren, das ich verwenden werde, das ständig Messwerte von einem Sensor nimmt. Dann versuche ich, die Ausgabe zu lesen (in diesem Fall "2000"
) aus dem C-Programm mit Unterprozess in Python:
#!usr/bin/python
import subprocess
process = subprocess.Popen("./main", stdout=subprocess.PIPE)
while True:
for line in iter(process.stdout.readline, ''):
print line,
aber das funktioniert nicht. Durch die Verwendung von print-Anweisungen wird die ausgeführt .Popen
Linie wartet dann an for line in iter(process.stdout.readline, ''):
bis ich Strg-C drücke.
Warum ist das? Dies ist genau das, was die meisten Beispiele, die ich gesehen habe, als Code haben, und dennoch wird die Datei nicht gelesen.
Gibt es eine Möglichkeit, es nur laufen zu lassen, wenn es etwas zu lesen gibt?
Es ist ein Problem mit der Blockpufferung.
Was folgt, ist eine für Ihren Fall erweiterte Version meiner Antwort auf Python: read streaming input from subprocess.communicate() question.
Korrigieren Sie den stdout-Puffer direkt im C-Programm
stdio
-basierte Programme werden in der Regel zeilengepuffert, wenn sie interaktiv in einem Terminal ausgeführt werden, und blockgepuffert, wenn ihre Standardausgabe in eine Pipe umgeleitet wird. Im letzteren Fall sehen Sie keine neuen Zeilen, bis der Puffer überläuft oder geleert wird.
Um Anrufe zu vermeiden fflush()
nach jedem printf()
aufrufen, könnten Sie eine zeilengepufferte Ausgabe erzwingen, indem Sie ganz am Anfang ein C-Programm aufrufen:
setvbuf(stdout, (char *) NULL, _IOLBF, 0); /* make line buffered stdout */
Sobald ein Zeilenumbruch ausgegeben wird, wird in diesem Fall der Puffer geleert.
Oder beheben Sie es, ohne die Quelle des C-Programms zu ändern
Es gibt stdbuf
Dienstprogramm, mit dem Sie den Pufferungstyp ändern können, ohne den Quellcode zu ändern, z.
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
Es sind auch andere Dienstprogramme verfügbar, siehe Deaktivieren Sie die Pufferung in der Pipe.
Oder verwenden Sie Pseudo-TTY
Um den Unterprozess glauben zu machen, dass er interaktiv läuft, könnten Sie verwenden pexpect
Modul oder seine Entsprechungen für Codebeispiele, die verwenden pexpect
und pty
Module finden Sie unter Python-Unterprozess readlines() hängt. Hier ist eine Variation der pty
dort bereitgestelltes Beispiel (es sollte unter Linux funktionieren):
#!/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)
Oder verwenden pty
über pexpect
pexpect
wickelt pty
Handhabung in Schnittstelle auf höherer Ebene:
#!/usr/bin/env python
import pexpect
child = pexpect.spawn("/.main")
for line in child:
print line,
child.close()
F: Warum nicht einfach eine Pipe (popen()) verwenden? erklärt, warum Pseudo-TTY nützlich ist.
Ihr Programm hängt nicht, es läuft nur sehr langsam. Ihr Programm verwendet gepufferte Ausgabe; das "2000\n"
Daten werden nicht sofort nach stdout geschrieben, werden es aber irgendwann schaffen. In Ihrem Fall kann es dauern BUFSIZ/strlen("2000\n")
Sekunden (wahrscheinlich 1638 Sekunden) bis zum Abschluss.
Nach dieser Zeile:
printf("2000\n");
hinzufügen
fflush(stdout);
Sehen Readline-Dokumente.
Dein Code:
process.stdout.readline
Wartet auf EOF oder einen Zeilenumbruch.
Ich kann nicht sagen, was Sie letztendlich versuchen, aber Ihrem printf einen Zeilenumbruch hinzuzufügen, z. printf("2000\n");
sollte Ihnen zumindest den Einstieg erleichtern.
fallen
while True
. Einmal diefor
-Schleife beendet; davon bekommst du nichtsprocess.stdout
mehr.– jfs
11. Dezember 2013 um 0:29 Uhr
Ich denke, die Antwort sollte in JF Sebastians Version geändert werden. Siehe meinen Kommentar dort warum.
– Scheintod
28. Dezember 2013 um 10:02 Uhr
Danke @Scheintod, guter Punkt.
– theo-braun
31. Dezember 2013 um 7:56 Uhr