Prozessausgabe on-the-fly formatieren

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
McEnroe
User
Beiträge: 5
Registriert: Montag 5. Februar 2007, 13:21

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?
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Wie wäre es mit cmake.readline() in der for schleife?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
McEnroe
User
Beiträge: 5
Registriert: Montag 5. Februar 2007, 13:21

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.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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"

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

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
McEnroe
User
Beiträge: 5
Registriert: Montag 5. Februar 2007, 13:21

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?
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()
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
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.
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()
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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:

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

@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())

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Könnten wir nun dazu übergehen, den ersten Parameter von subprocess.Popen generell als Liste/Tupel zu übergeben? Wunderbar, danke...
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Wo ist denn dein Problem?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

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* ;)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

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?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

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...
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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"

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

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.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
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. :-)
Antworten