Seite 1 von 2

Prozessausgabe on-the-fly formatieren

Verfasst: Montag 5. Februar 2007, 13:31
von McEnroe
Ich habe das folgende Problem:
Ich schreibe ein Script, das KDE aus den Quellen baut. Dazu müssen Programme wie "cmake", "make", "make install" usw. aufgerufen werden. Dazu genügt ein subprocess.call(). Jetzt will ich die Ausgabe aber formatieren (konkret: um einen Tabulator einrücken). Wenn ich das mit

Code: Alles auswählen

cmake = subprocess.Popen(cmakecall, stdout=subprocess.PIPE)
for line in cmake.communicate()[0].split("\n"):
   print "\t", line
return cmake.wait()
mache wird zwar alles formatiert, aber die Ausgabe erfolgt erst wenn der Prozess beendet ist (bei einem "make" - sehr lang).

Wie kriege ich die Formatierung "on-the-fly" hin, quasi "live" während der Prozess noch läuft hin?

Verfasst: Montag 5. Februar 2007, 13:50
von jens
Wie wäre es mit cmake.readline() in der for schleife?

Verfasst: Montag 5. Februar 2007, 14:06
von McEnroe
Du meinst wahrscheinlich cmake.stdout.readline()...
Das gibt aber nur die erste Zeile aus.
readlines() wartet bis der Prozess fertig ist und gibt es erst dann aus.

Verfasst: Montag 5. Februar 2007, 14:28
von jens
Hm... Kommt IMHO auf den child-prozess an... Ob er ein flush macht...
Das geht bei mir jedenfalls (Windows):

Code: Alles auswählen

import sys, subprocess

command = "ping heise.de"

process = subprocess.Popen(
    command,
    shell   = True,
    stdout  = subprocess.PIPE
)
while 1:
    line = process.stdout.readline()
    if line == "": break
    sys.stdout.write(line)

print "ENDE"

Verfasst: Montag 5. Februar 2007, 14:37
von sape
jens hat geschrieben:Hm... Kommt IMHO auf den child-prozess an... Ob er ein flush macht...
Das ist der Punkt!

Das ist eben das Problem an dem ich auch gescheitert bin und bisher keine Brauchbare Lösung habe: http://www.python-forum.de/topic-9133.html
Entweder der gestartete Prozess puffert oder nicht. Wen er puffer hat man eben Pech gehabt.

lg

Verfasst: Montag 5. Februar 2007, 14:43
von McEnroe
Wie wird denn bestimmt ob ein Prozess puffert?

Und die Sache mit der Pufferung ist die, dass subprocess.call() bis auf die Formatierung ja genau das tut was ich will....

@jens:
1. Was bewirkt das shell=true eigentlich?
2. Was mach ich wenn in der Ausgabe Leerzeilen auftauchen sollten?

Verfasst: Montag 5. Februar 2007, 15:07
von BlackJack
jens hat geschrieben:

Code: Alles auswählen

while 1:
    line = process.stdout.readline()
    if line == "": break
    sys.stdout.write(line)
Können wir das bitte etwas "pythonischer" schreiben:

Code: Alles auswählen

for line in process.stdout:
    sys.stdout.write(line)
    sys.stdout.flush()

Verfasst: Montag 5. Februar 2007, 15:11
von jens
BlackJack hat geschrieben:Können wir das bitte etwas "pythonischer" schreiben:
Ja, das sieht besser aus... Tut's aber nicht so richtig. Denn dann sehe ich die Ausgaben erst, wenn der Prozess fertig ist ;)

Verfasst: Montag 5. Februar 2007, 15:14
von BlackJack
McEnroe hat geschrieben:Wie wird denn bestimmt ob ein Prozess puffert?
Das bestimmst Du entweder selbst beim öffnen einer Datei, bzw. hat das an dieser Stelle jedes Programm selbst in der Hand, oder die C-Bibliothek entscheidet anhand des Dateityps: Wenn es eine Datei ist, die direkt mit einem Terminal verbunden ist, dann wird zeilenweise gepuffert, sonst gibt's einen Blockpuffer.
1. Was bewirkt das shell=true eigentlich?
Es wird eine Shell gestartet, die dann das Programm startet. Wozu diese Indirektion in diesem Fall gut sein soll, weiss ich nicht.
2. Was mach ich wenn in der Ausgabe Leerzeilen auftauchen sollten?
Leerzeilen sind nicht wirklich leer sondern bestehen aus einem Zeilenende-Zeichen. Die leere Zeichenkette kommt wirklich nur wenn die Datei gar nichts mehr hergibt. Bei Pipes also nur dann, wenn die Gegenseite die Datei geschlossen hat. Aber eine ``for``-Schleife über die Datei ist sowieso etwas schöner.

Verfasst: Montag 5. Februar 2007, 15:17
von BlackJack
jens hat geschrieben:
BlackJack hat geschrieben:Können wir das bitte etwas "pythonischer" schreiben:
Ja, das sieht besser aus... Tut's aber nicht so richtig. Denn dann sehe ich die Ausgaben erst, wenn der Prozess fertig ist ;)
Das könnte eventuell an der Pufferung von `file.xreadlines()` liegen, obwohl ich das noch nicht so ganz glaube.

Dann schreib's halt so:

Code: Alles auswählen

for line in iter(process.stdout.readline, ''):
    sys.stdout.write(line)
    sys.stdout.flush()

Verfasst: Montag 5. Februar 2007, 15:17
von jens
Bei mir unter Windows in SciTE öffnet sich bei shell=False ein cmd-Fenster. Bei shell=True halt nicht...

Lustig ist ein command = "chkdsk c:"... Der erste Teil sieht man Live, macht also wohl ein flush... Die beiden Nachfolgenden Teile nicht... Sehr konsistent :roll:

Verfasst: Montag 5. Februar 2007, 15:19
von jens
@BlackJack: Ja, so geht's...

Zum einfügen von TABs:

Code: Alles auswählen

for line in iter(process.stdout.readline, ''):
    sys.stdout.write("\t%s\n" % line.rstrip())

Verfasst: Montag 5. Februar 2007, 18:17
von Leonidas
Könnten wir nun dazu übergehen, den ersten Parameter von subprocess.Popen generell als Liste/Tupel zu übergeben? Wunderbar, danke...

Verfasst: Montag 5. Februar 2007, 18:24
von jens
Wo ist denn dein Problem?

Verfasst: Montag 5. Februar 2007, 18:28
von sape
Warum Leo?
http://docs.python.org/lib/node529.html
The executable argument specifies the program to execute. It is very seldom needed: Usually, the program to execute is defined by the args argument.[...]
Wo steht hier das ein iterable erwartet wird?
[...]Wunderbar, danke...[...]
*Eine-Tasse-Tee-rübereich-zur-Entspannung* ;)

Verfasst: Montag 5. Februar 2007, 18:37
von Leonidas
sape hat geschrieben:
The executable argument specifies the program to execute. It is very seldom needed: Usually, the program to execute is defined by the args argument.[...]
Wo steht hier das ein iterable erwartet wird?
Ich spreche doch gar nicht von ``executable``. Aber es ist auf der selben Seite, hier:
http://docs.python.org/lib/node529.html hat geschrieben:args should be a string, or a sequence of program arguments. The program to execute is normally the first item in the args sequence or string, but can be explicitly set by using the executable argument.
Wenn wir noch mal darüber nachdenken (oder einfach in den Quellcode schauen) warum dort eine Liste akzeptiert wird kommen wir zu etwas sehr praktischem: subprocess.Popen kann, wenn man ihm statt einem String eine Liste übergibt, selbst quoten, ansonsten muss man das selbst machen. Und warum man besser quoten lässt statt selber quotet (bzw. es sogar vergisst), muss ich nicht erklären, oder?

In diesem Beispiel ist es ja egal, aber ich finde es ist besser Beispiele zu bringen, die problemloser sind wenn man auf die Idee kommt sie zu erweitern. Was wenn man Leerzeichen in den Argumenten hat?

Verfasst: Montag 5. Februar 2007, 19:01
von birkenfeld
Leonidas hat geschrieben:Könnten wir nun dazu übergehen, den ersten Parameter von subprocess.Popen generell als Liste/Tupel zu übergeben? Wunderbar, danke...
Bis 2.5.1 bitte nur Listen, siehe http://python.org/sf/1357915...

Verfasst: Dienstag 6. Februar 2007, 08:05
von jens
Leonidas hat geschrieben:Könnten wir nun dazu übergehen, den ersten Parameter von subprocess.Popen generell als Liste/Tupel zu übergeben?
Ah, sorry, hab gestern die zwei Wörter "den ersten" überlesen ;)

Also hier die Optimal Lösung:

Code: Alles auswählen

import sys, subprocess

command = ["ping", "heise.de"]

process = subprocess.Popen(
    command,
    shell   = True,
    stdout  = subprocess.PIPE
)
for line in iter(process.stdout.readline, ''):
    sys.stdout.write("\t%s\n" % line.rstrip())

print "ENDE"

Verfasst: Dienstag 6. Februar 2007, 17:18
von Leonidas
birkenfeld hat geschrieben:Bis 2.5.1 bitte nur Listen, siehe http://python.org/sf/1357915...
Ah, stimmt - ich bin sogar mal über diesen Bugreport gestolpert.

Verfasst: Dienstag 6. Februar 2007, 19:38
von BlackJack
Hat das Windows-``ping`` auch eine Option ``-c NUM`` um die Anzahl der Pings anzugeben? Wenn man die einbaut, dann kommt das Skript auch unter Linux irgendwann zu einem Ende und bombardiert den armen Heise-Server nicht non-stop. :-)