Ausgabe von Programm einlesen

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.
Antworten
samy-delux
User
Beiträge: 44
Registriert: Donnerstag 26. April 2007, 19:23

Hey Leute,

Ich würde gerne in meinem Python Programm ein anderes Programm aufrufen und davon den output einfangen und in einer Variable speichern!
Ich habe jetzt eine halbe Stunde gesucht und die Module posix, subprocess und os ausprobiert, doch ich bekomme es einfach nicht gebacken!

Die Sache ist doch bestimmt ganz einfach oder?

so long,
Samy
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Hallo,

das kannst Du ganz einfach mit popen() machen! Hab grad kein Python zur Verfügung, aber ist echt einfach. Guck mal in der Doku bei "os" nach. iirc steht da was dazu. Ist eigentlich wie ne Datei, also ca. folgendes Schema (ungetestet):

Code: Alles auswählen

p = popen("dein_programm")
out = p.read()
p.close()
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Ab Python 2.4 sollte man subprocess verwenden: [wiki]Neue Features#Subprocess[/wiki]
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
samy-delux
User
Beiträge: 44
Registriert: Donnerstag 26. April 2007, 19:23

Mit subprocess würde ich es gerne machen, doch folgender Code funktioniert nicht:

Code: Alles auswählen

    process = subprocess.Popen(["du", "--max-depth=0 "+dir], stdout=subprocess.PIPE)
    process.wait()
    print process.stdout.read()
Die Ausgabe ist:

Code: Alles auswählen

156720  .
Was mache ich da falsch?

so long,
Samy
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Rebecca hat geschrieben:Ab Python 2.4 sollte man subprocess verwenden: [wiki]Neue Features#Subprocess[/wiki]
Wußte ich noch nicht. Man lernt nie aus :)
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Bei mir klappt es so, man beachte den Unterschied bei der Parameteruebergabe fuer du:

Code: Alles auswählen

>>> process = subprocess.Popen(["du", "--max-depth=0", "/home/rbreu"], stdout=subprocess.PIPE)
>>> process.wait()
>>> print process.stdout.read()
18360840        /home/rbreu
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
samy-delux
User
Beiträge: 44
Registriert: Donnerstag 26. April 2007, 19:23

ok, danke!
Es lag an der übergabe der Argumente!
samy-delux
User
Beiträge: 44
Registriert: Donnerstag 26. April 2007, 19:23

Hm, ich habe gerade wieder ein Problem:
Ich will gerne alle Dateien die es in einem Verzeichnis + Unterverzeichnissen gibt einlesen. Das geht auf der Konsole ja ganz einfach mit:

Code: Alles auswählen

find /directory -name '*'
Insgesamt erzeugt das 2006 Zeilen (mit einem "wc -l" gemessen) und funktioniert auf der Konsole wunderbar.

Wenn ich nun jedoch folgenden Python Code probiere das gleiche machen zu lassen, macht er einfach nichts und das für mehrere Stunden:

Code: Alles auswählen

import subprocess

dir = "/directory"

process = subprocess.Popen(["find",  dir, "-name", "*"], stdout=subprocess.PIPE)
process.wait()
print process.stdout.read()
Wenn ich allerdings das Suchmuster etwas eingrenze, dann geht das ganze wunderbar. Ich habe es mit "*html" als Suchmuster versucht was mit 434 Ergebnisse liefert. Das funktioniert dann sowohl auf der Shell als auch in Python wunderbar!

Irgendwas läuft da schief!
pug
User
Beiträge: 16
Registriert: Dienstag 4. September 2007, 17:00

Hallo

ich vermute, dass Du den Parameter * noch in Hochkommas schreiben musst also: "'*'".
Da Du ja bei deinem ursprünglichen Befehl auch den * in Hochkomma hast.

Code: Alles auswählen

import subprocess

dir = "/directory"

process = subprocess.Popen(["find",  dir, "-name", "'*'"], stdout=subprocess.PIPE)
process.wait()
print process.stdout.read()

Grüße
Pug
BlackJack

Die Vermutung habe ich auch, denn:

Code: Alles auswählen

bj@s8n:~$ find /directory -name *
find: paths must precede expression
Usage: find [-H] [-L] [-P] [path...] [expression]
Und diese Meldung kommt nicht auf `process.stdout` an, sondern auf `process.stderr`. Letzteres liest Du nie aus, damit beendet sich der Prozess nie, und das wiederum führt dazu, dass Du beim Lesen von `process.stdout` bis zum Sankt Nimmerleins-Tag warten kannst.
lunar

Das sieht nur auf der Shell so aus (da ist der Asterik ja ein Sonderzeichen und muss korrekt gequoted werden). Allerdings umgeht subprocess die Shell und nutzt fork/execvp, um ein neuen Prozess zu starten. Die Argumente in der Liste kommen genau so, wie sie dastehen, bei find an:

Code: Alles auswählen

[lunar@nargond]-[10:10:34] >> /home/lunar
[2]--> subprocess.call(['find', '/home/lunar', '-name', '*'])
/home/lunar
/home/lunar/.htoprc
/home/lunar/Kalender.ics~
/home/lunar/.recently-used.xbel
/home/lunar/.bogofilter
/home/lunar/.bogofilter/wordlist.db
/home/lunar/.ktorrent.lock
...
Es funktioniert also auch ohne Quoting. Im Gegenteil, ich glaube sogar, dass es mit Hochkomma gar nicht funktioniert, weil find dann nach Dateien sucht, die dieses Hochkomma enthalten (find sieht das Hochkomma ja, da die Shell nicht dazwischen funkt).

Edit: wait scheint bei meinen Versuchen ebenfalls zu hängen, allerdings sieht subprocess ja eigentlich auch eine andere Methode vor, um mit dem Unterprozess zu kommunizieren:

Code: Alles auswählen

process = subprocess.Popen(['find', '/directory', '-name', '*'], stdout=subprocess.PIPE)
stdout = process.communicate()[0]
print stdout
Damit funktioniert es auch.
Zuletzt geändert von lunar am Sonntag 23. September 2007, 09:29, insgesamt 2-mal geändert.
BlackJack

Argh, stimmt. :oops: Ist noch zu früh. :-)
samy-delux
User
Beiträge: 44
Registriert: Donnerstag 26. April 2007, 19:23

Super, so funktioniert es!
Vielen Dank für die super Hilfe hier!

Nochmal ne kurze Rückfrage: Es ist also generell besser den Output mit "process.communicate()[0]" zu holen, anstatt erst "process.wait()" zu machen und dann den Output anzufragen?
lunar

Nein.
Python Doku hat geschrieben:Note: The data read is buffered in memory, so do not use this method if the data size is large or unlimited.
Wenn du große Mengen an Daten aus der Pipe lesen willst, dann solltest du communicate nicht nutzen. wait() funktioniert offenbar allerdings auch nicht. Ich denke mal, dass das daran liegt, dass der Pufferspeicher der Pipe zuläuft und find so nichts mehr auf stdout schreiben kann und deswegen einfriert, bis du aus stdout liest.

Um richtig große Mengen an Daten einzulesen, würde sich also vielleicht eher sowas anbieten:

Code: Alles auswählen

process = subprocess.Popen(['find', '/directory', '-name', '*'], stdout=subprocess.PIPE, bufsize=1)
while True:
    line = process.stdout.readline()
    if not line:
        break
    # die Zeile hier verarbeiten
Dadurch sparst du dir das Anlegen einer endlosen Liste. Wenn der Speicherverbrauch allerdings egal ist, dann kannst imho auch communicate verwenden.
BlackJack

Direkt über `process.stdout` iterieren geht nicht?
lunar

BlackJack hat geschrieben:Direkt über `process.stdout` iterieren geht nicht?
Doch, wahrscheinlich schon ;) Hab irgendwie nicht daran gedacht...

Auf die Uhrzeit kann ich das jetzt nicht schieben... aber vielleicht auf den fehlenden Kaffee heute Nachmittag ;)
Antworten