2012-09-04 6 views
26

Possible Duplicate:
Wrap subprocess' stdout/stderrKönnen Sie einen Python-Subprozess stdout und stderr wie gewohnt ausgeben, aber auch die Ausgabe als String erfassen?

In this question, hanan-n gefragt, ob es möglich war, eine Python subprocess zu haben, die auf der Standardausgabe ausgibt, während auch die Ausgabe in einer Zeichenfolge für eine spätere Verarbeitung zu halten. Die Lösung war in diesem Fall über jede Ausgangsleitung Schleife und drucken Sie sie manuell:

output = [] 
p = subprocess.Popen(["the", "command"], stdout=subprocess.PIPE) 
for line in iter(p.stdout.readline, ''): 
    print(line) 
    output.append(line) 

Diese Lösung ist jedoch nicht auf den Fall nicht verallgemeinern, wo Sie dies sowohl für stdout und stderr tun wollen, während die Befriedigung folgende:

  • die Ausgabe von stdout/stderr zu den übergeordneten Prozess gehen sollte stdout/stderr bzw.
  • die Ausgabe in Echtzeit so viel wie möglich getan werden sollte (aber ich nur Zugriff auf die Saiten müssen bei das Ende)
  • der o rder zwischen stdout und stderr Zeilen sollte nicht geändert werden (ich bin mir nicht ganz sicher, wie das auch funktionieren würde, wenn der Subprozess seine stdout und stderr caches in verschiedenen Intervallen leert; Lassen Sie uns jetzt davon aus, dass wir alles in schöne Stücke erhalten, die durchgezogenen Linien enthalten?)

ich durch die subprocess documentation sah, konnte aber nichts finden, dass dies erreicht werden kann. Der nächste, den ich finden könnte, ist stderr=subprocess.stdout hinzuzufügen und die gleiche Lösung wie oben zu verwenden, aber dann verlieren wir die Unterscheidung zwischen regulärer Ausgabe und Fehlern. Irgendwelche Ideen? Ich nehme an, die Lösung - falls es eine gibt - beinhaltet asynchrone Lesevorgänge an p.stdout und p.stderr. Hier

ist ein Beispiel dafür, was Ich mag würde tun:

p = subprocess.Popen(["the", "command"]) 
p.wait() # while p runs, the command's stdout and stderr should behave as usual 
p_stdout = p.stdout.read() # unfortunately, this will return '' unless you use subprocess.PIPE 
p_stderr = p.stderr.read() # ditto 
[do something with p_stdout and p_stderr] 
+0

Ich sehe nicht, wie diese Technik (umhüllen keine IO-Funktionen und Stream-Objekte) nicht verallgemeinert? – ninjagecko

+0

Wie empfehlen Sie, dass ich den Subprozess in Echtzeit auf stdout und stderr drucke, während am Ende die Ausgabe in einer Zeichenkette bleibt? – pflaquerre

Antwort

37

Dieses Beispiel scheint für mich zu arbeiten:

# -*- Mode: Python -*- 
# vi:si:et:sw=4:sts=4:ts=4 

import subprocess 
import sys 
import select 

p = subprocess.Popen(["find", "/proc"], 
    stdout=subprocess.PIPE, stderr=subprocess.PIPE) 

stdout = [] 
stderr = [] 

while True: 
    reads = [p.stdout.fileno(), p.stderr.fileno()] 
    ret = select.select(reads, [], []) 

    for fd in ret[0]: 
     if fd == p.stdout.fileno(): 
      read = p.stdout.readline() 
      sys.stdout.write('stdout: ' + read) 
      stdout.append(read) 
     if fd == p.stderr.fileno(): 
      read = p.stderr.readline() 
      sys.stderr.write('stderr: ' + read) 
      stderr.append(read) 

    if p.poll() != None: 
     break 

print 'program ended' 

print 'stdout:', "".join(stdout) 
print 'stderr:', "".join(stderr) 

Im Allgemeinen kann jede Situation, wo man Sachen mit mehreren Filedeskriptoren zur gleichen Zeit tun wollen, und Sie don Ich weiß nicht, welcher Stoff für Sie zu lesen ist, Sie sollten wählen oder etwas Ähnliches (wie ein Twisted-Reaktor) verwenden.

+1

Die Reihenfolge ist möglicherweise nicht * genau * die gleiche wie die ursprüngliche, da 'stdout' und' stderr' beide Daten haben, wenn wir 'select' aufrufen, aber das ist so nah an einer Lösung, wie ich es gesehen habe weit. Vielen Dank! – pflaquerre

+0

Was meinst du mit der Bestellung? In dem Beispiel, das ich gebe, wenn es Daten gibt, die von beiden zu lesen sind, wird es zuerst mit stdout umgehen, da sein fd normalerweise niedriger ist als stderrs, und select sollte es in der richtigen Reihenfolge zurückgeben. Wenn das nicht der Fall oder nicht garantiert ist, können Sie einfach den Code ändern, um etwas wie p.stdout.fileno() in ret [0] zu tun: und dann das gleiche für stderr, das sollte die Reihenfolge garantieren. –

+0

Ich spreche über die Reihenfolge, in der die Streams geschrieben wurden. Zum Beispiel könnte der Unterprozess zuerst in stderr schreiben und dann etwas anderes in stdout schreiben, aber 'select' würde immer noch von stdout vor stderr lesen, was nicht die gleiche Reihenfolge ist wie das, was tatsächlich passiert ist. Im schlimmsten Fall schreibt der Subprozess so viel, dass nichts in stderr verschoben wird, bis der Prozess beendet ist. (Es sei denn, ich missverstand, wie Select funktioniert?) – pflaquerre

2

Erstellen Sie zwei Leser, wie oben, eine für stdout eine für stderr und starten jeweils in einem neuen Thread. Dies würde an die Liste in ungefähr der gleichen Reihenfolge angehängt werden, in der sie von dem Prozess ausgegeben wurden. Pflegen Sie zwei separate Listen, wenn Sie möchten.

dh

p = subprocess.Popen(["the", "command"]) 
t1 = thread.start_new_thread(func,stdout) # create a function with the readers 
t2 = thread.start_new_thread(func,stderr) 
p.wait() 
# your logic here 
+0

Zeit zu verwenden, um Threads zu sortieren, ist eine schlechte Idee. – Dani

+0

@Dani - Das waren zwei verschiedene Ideen, die Zeit, die man nicht threading ... – dfb

+0

Sie sagen, Sie können eine Deadlock mit beiden warten() und poll() in Kombination mit PIPE von stdout (und stderr) . – danger89

9

auf Konsole und Erfassung in einem String stdout/stderr eines subprocess in einer tragbaren Weise drucken:

from StringIO import StringIO 

fout, ferr = StringIO(), StringIO() 
exitcode = teed_call(["the", "command"], stdout=fout, stderr=ferr) 
stdout = fout.getvalue() 
stderr = ferr.getvalue() 

wo teed_call() in Python subprocess get children's output to file and terminal?

definieren Sie alle Dateiähnliche Objekte verwenden könnten (.write() Methode).

+0

Poster möchte Daten von stdout/stderr so verarbeiten, wie sie kommen, nicht am Ende des Befehls. –

+0

@ThomasVanderStichele: Klicken Sie auf den Link und schauen Sie sich die Definition 'call()' an. Es zeigt stdout/stderr so schnell wie möglich an. Es ist nicht "subprocess.call()". – jfs

+6

Dies funktioniert nicht, zumindest nicht immer: 'AttributeError: StringIO-Instanz hat kein Attribut 'fileno'' – KomodoDave

Verwandte Themen