subprocess Ausgaben in "Echtzeit" verabeiten

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

Edit (jens): Abgetrennt von: http://www.python-forum.de/topic-16346.html

Was haltet ihr hiervon:

Code: Alles auswählen

# -*- coding: utf-8 -*-

import sys, subprocess

IS_WIN = (sys.platform == "win32")

cmd = ["ping", "127.0.0.1"]
if not IS_WIN:
    cmd.append("-c 5")

print " ".join(cmd)
p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE)

line = ""
while p.poll() is None:
    char = p.stdout.read(1)
    if char == "\n":
        print line
        line = ""
    elif IS_WIN and char == "\r":
        continue
    else:
        line += char

print "--- END ---"
Zuletzt geändert von jens am Freitag 24. Oktober 2008, 11:09, insgesamt 1-mal geändert.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

@jens: Nun muss ich mich aber echt zusammenreissen um nichts beleidigendes zu antworten.

Du hast das Problem vom OP anscheinend nicht verstanden und ich weiss nicht einmal wofür dass da eine Lösung sein soll.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Sorry, ich hab den Thread nicht komplett gelesen :oops:

Ich hatte selber nach einer Lösung gesucht und bin auf diesen Thread gestoßen. Ich wollte eigentlich die Ausgaben von einem subprocess lesen, aber gleichzeitig auch anzeigen lassen. Dachte hier geht's um das selbe...

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:

So, ich hab es mal Abgetrennt vom Ursprünglichen Thread: http://www.python-forum.de/topic-16346.html

Irgendwie ist es auch blödsinn mit read(1) zu arbeiten. readline macht da mehr Sinn:

Code: Alles auswählen

# -*- coding: utf-8 -*-

import os, sys, subprocess

def auswertung(line):
    """
    Hier kann die Ausgabe verarbeitet werden.
    """
    out = "[%-77s]" % line.strip()
    print out

def test(cmd):
    print " ".join(cmd)
    print "-"*79
    p = subprocess.Popen(
        cmd,
        stdout=subprocess.PIPE,
        shell=False,
    )
    while True:
        line = p.stdout.readline()
        auswertung(line)
        if line=="":
            break


if sys.platform == "win32":
    switch = "-n"
else:
    switch = "-c"

cmd = ["ping", switch, "3", "127.0.0.1"]
test(cmd)


print "--- END ---"
EDIT: Das könnte man auch noch kombinieren mit einem Timeout, siehe:
http://www.python-forum.de/post-96795.html#96795
http://trac.pylucid.net/browser/trunk/p ... rocess2.py

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:

Was ich mich nun Frage, kann man ein Process starten und auf ihn warten. Dabei sollen seine Ausgaben in der shell Sichtbar sein. Wenn der Process fertig ist, brauche ich aber dann auch die Ausgaben in Python.

Wie kann man das machen?

Mir fällt da nur wieder diese .read(1) Lösung ein:

Code: Alles auswählen

    p = subprocess.Popen(
        cmd,
        stdout=subprocess.PIPE,
        shell=False,
    )
    buffer = ""
    while True:
        char = p.stdout.read(1)
        if char=="":
            break

        buffer += char
        sys.stdout.write(char)
        sys.stdout.flush()

    auswertung(buffer)

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Hi

Damit funktionierts nicht?
http://www.python-forum.de/post-112880.html#112880

Gruss
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Das ist allerdings eine Windows-only Lösung. Nicht so schön...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Für eine Linux-Lösung siehe den Post über dem zitierten.
MfG
HWK
Qubit
User
Beiträge: 128
Registriert: Dienstag 7. Oktober 2008, 09:07

jens hat geschrieben:Was ich mich nun Frage, kann man ein Process starten und auf ihn warten. Dabei sollen seine Ausgaben in der shell Sichtbar sein. Wenn der Process fertig ist, brauche ich aber dann auch die Ausgaben in Python.

Wie kann man das machen?
Mal zum Verständnis:
Prozesse haben 3 Standardpipes: IN,OUT,ERR.
"Popen" startet einen Prozess und kann nun die Pipes dieses Prozesses in dessen Prozessraum setzen (über PIPE). Die gestartete Applikation schreibt i.a. in STDOUT. Ändert man diese PIPE schreibt er auch nichts mehr auf die Konsole. Belässt man ihn, werden keine Ausgaben an "subprocess" gepiped.
Entweder öffnet die Applikation zusätzliche PIPES, über die man an Daten rankommt oder man muss die Daten selbst duplizieren und den Descriptor auf den STDOUT des (Sub-) Prozesses setzen, ähnlich wie es "tee" unter Unix macht.
Vielleicht kann man "subprocess" dahingehend patchen, so dass STDOUT dupliziert wird und eine PIPE an "subprocess" hängt. Standardmäßig ist das aber nicht implementiert ;-)
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Also ich hab mir den subprocess.py code schon mal angesehen. Ich hab in der Richtung nicht wirklich was finden können.

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:

Das eigentliche Problem ist, wenn das aufgerufene Programme kein stdout.flush() macht, geht die "Echtzeitanzeige" nicht...
rayo hat geschrieben:Damit funktionierts nicht?
http://www.python-forum.de/post-112880.html#112880
...auch damit nicht :(

Beispiel, zum selber testen hier: http://paste.pocoo.org/show/90128/

Irgendwelche Lösungen dafür???

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
lunar

Nein. Wenn die Daten nicht in der Pipe, sondern noch im Puffer liegen, gibt es nichts, was man lesen könnte.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Das Leuchtet mir eigentlich ein. Aber: In der Konsole sehe ich ja auch die Ausgabe und das ohne Verzögerung.

Könnte also eine Lösung darin bestehen, eine Konsole zu emulieren???

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:

Das Problem wird in der FAQ von Pexpect ganz gut erklärt: http://www.noah.org/wiki/Pexpect#Q:_Why ... 8.29.29.3F

So wie ich es verstehe:
Oft nutzten Programme die Standard C IO Library (stdio.h). Diese nutzt ein eigenen Puffer. Wenn das Programm nicht explizit ein stdout.flush() macht, dann wird erstmal der "interne" Puffer voll geschrieben und erst dann ausgegeben. Dabei weiß stdio.h ob es in eine Pipe schreibt oder in ein Terminal. In eine Pipe kommt ein "Block Puffer" zum einsatz, der größer ist, als der normale "Line Puffer".
Somit bekommt man aus der Pipe erst Daten wenn der Puffer voll ist, oder das Programm beendet ist.

Die Frage ist nun, kann man irgendwie die Puffer Größe beeinflussen und auf 1-Zeichen setzten, oder so tun als wenn man ein Terminal und nicht eine Pipe ist???
Es gibt die Angabe "bufsize" bei subprocess. Aber egal ob auf 0 oder 1 gesetzt, das änder nix, warum?

Kann man evtl. was mit startupinfo und creationflags machen???

Zum Thema Pseudo-Terminal gibt es unter Windows anscheinend nicht wirklich, siehe: http://www.noah.org/wiki/Pexpect#pty_module
Ich hab auch diesbezüglich gesucht, aber nichts gefunden. Irgendwie kann man was mit Cygwin machen, aber das scheint mir sehr Aufwendig zu sein.

EDIT: Hab das ganze mal unter Linux getestet. Eigentlich gibt es da das selbe Problem!
Zuletzt geändert von jens am Mittwoch 5. November 2008, 13:49, insgesamt 1-mal geändert.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

Die Angabe zu `bufsize` ändert nichts am Puffer des *anderen* Programms, sondern betrifft den Puffer auf "Deiner Seite" der Pipe.

Zum Terminal-Emulieren kann man Pexpect nehmen. Und ich glaube Rebecca hatte so etwas mal selbst implementiert und hier ins Forum gestellt.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Nein, Pexpect kann nur unter Linux ein Pseudo Terminal erstellen. Unter Windows nicht, siehe:
Pexpect does not currently work on the standard Windows Python (see the pty requirement); however, it seems to work fine using Cygwin. It is possible to build something like a pty for Windows, but it would have to use a different technique that I am still investigating. I know it's possible because Libes' Expect was ported to Windows. If you have any ideas or skills to contribute in this area then I would really appreciate some tips on how to approach this problem.
http://www.noah.org/wiki/Pexpect#pty_module

Es gibt ja diese zwei arten an Puffer in der stdio.h: Line-Buffer und "Block-Puffer" und die stdio.h weiß ja ob es in einer Pipe oder in einem echten Terminal schreibt. Man muss es also so drehen, das stdio.h statt der Pipe ein Terminal sieht... Aber wie geht das?

Im Netz finde ich immer nur Leute/Beiträge die das selbe Problem haben, aber nirgends eine Lösung dafür...

Evtl. müßte es mit "PyConsole" funktionieren: http://code.google.com/p/pyconsole/
Das Demo Filmschen sieht nett aus: http://www.plan10.com/PyConsole/demo_wx.htm
pyconsole_wx.py funktioniet bei mit allerdings nicht. Wenn man ein Kommando eingibt, wird es ausgefügt (Kann man im Taskmanager sehen) aber die Ausgabe sieht man nicht in der Konsole :(

EDIT: Vielleicht mal mit externen Tools probieren. Aber z.B. tee.exe aus http://sourceforge.net/projects/unxutils macht zwar im Prinzip was es soll, aber man sieht auch erst am Ende die Ausgaben. Ist ja auch nur eine Pipe Umleitung...

EDIT2: Crosspost:
http://python.net/pipermail/python-de/2 ... 08990.html

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Hi, ich muss diesen Beitrag mal wieder ausgraben weil ich gerade genau an diesem Problem knabbere.

Ich möchte (unter Linux) ein Programm starten und dessen Ausgabe auswerten. Das dürfte mit Pexpect schon einigermaßen funktionieren. Problem ist jetzt, das das CLI Programm eine Art Fortschrittsbalken mit ASCII Zeichen ausgibt was ich auswerten möchte um es in einem grafischen Balken darzustellen.

Kann man mit Pexpect zeichenweise lesen? So wie ich das bisher sehe geht das anscheinend nicht.
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

So, ich habe mir den Sourcecode von pexpect.py angeschaut und bin etwas schlauer geworden. Es gibt zb die Methoden read() und readline().

Ich habe zuerst versucht mit readline() zu arbeiten. Das Problem ist, das readline() nach \r\n sucht. Bei dem Programm, das ich auswerten möchte, habe ich jetzt das Problem das es den Fortschrittsbalken zwar ausgibt, aber dazu nutzt das Programm nur \r, damit die gleiche Zeile immer wieder überschrieben wird. Man hat also die komplette Ausgabe des Fortschrittsbalken in einer Zeile.

Ich habe jetzt mit read() eine Funktion gebastelt

Code: Alles auswählen

import pexpect
c = pexpect.spawn ('/usr/bin/avrdude -pm128 -U flash:r:test.hex:i')
#c = pexpect.spawn ('/usr/bin/avrdude -pm128')

h=""
while True:
    s=c.read(1)
    if not s:
        break
    elif "\r" in s:
        print repr(h)
        h=""
    else:
        if not "\n" in s:
            h=h+s
Der Fortschrittsbalken sieht so aus

Code: Alles auswählen

Reading |                                                    | 0% 0.00s
Reading | #                                                  | 1% 0.44s
Reading | #                                                  | 2% 0.80s
Reading | ##                                                 | 3% 1.17s
Reading | ##                                                 | 4% 1.54s
Reading | ###                                                | 5% 1.90s
Reading | ###                                                | 6% 2.27s
.
.
.
Reading | #################################################  | 97% 36.38s
Reading | #################################################  | 98% 36.74s
Reading | ################################################## | 99% 37.11s
Reading | ################################################## | 100% 37.48s
Ich nehme an, das andere Programme, die einen Fortschritt immer in der gleichen Zeile ausgeben, genauso vorgehen. Man kann mit der Methode expect() hier anscheinend wenig anfangen sondern muss mit read(1) jedes Zeichen einzeln holen und selbst auswerten.

Vielleicht hilft es ja dem einen oder anderen weiter
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Habe gerade http://ipython.scipy.org gefunden. Eine shell in Python. Es gibt das auch für Windows: http://ipython.scipy.org/moin/IpythonOnWindows
Für Windows wird das genutzt: http://sourceforge.net/projects/console

Vielleicht kann man damit was anfangen?

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

IPython ist keine Shell sondern ein erweiterter Python-Interpreter und wenn du die Forumssuche nutzt, merkst du auch, dass das hier jeder zweite nutzt. Ist ja auch eine praktische Sache, bunte Prompts, rote Fehlermeldungen, paar magische Features wie die Möglichkeit Shell-Kommandos direkt durchzuführen, Profiler zu starten etc.

Die Python-Shell ist nur ein Nebenprodukt von IPython.
Antworten