2016-05-28 6 views
3

Ich habe folgenden Python 2 Beispiel-Code, den ich mit Python kompatibel machen will 3:Python 2 bis 3 Umwandlung: Iterieren über die Leitungen in subprocess stdout

call = 'for i in {1..5}; do sleep 1; echo "Hello $i"; done' 
p = subprocess.Popen(call, stdout=subprocess.PIPE, shell=True) 
for line in iter(p.stdout.readline, ''): 
    print(line, end='') 

Das funktioniert gut in Python 2, aber in Python 3 p.stdout erlaubt mir nicht, eine Codierung anzugeben und das Lesen wird Byte-Zeichenfolgen anstelle von Unicode zurückgeben, so dass der Vergleich mit '' immer false zurückgibt und iter wird nicht gestoppt. This issue scheint zu implizieren, dass es in Python 3.6 eine Möglichkeit gibt, diese Kodierung zu definieren.

Für jetzt habe ich den iter Aufruf geändert, um zu stoppen, wenn es eine leere Bytezeichenfolge iter(p.stdout.readline, b'') findet, die in 2 und 3 zu funktionieren scheint. Meine Fragen sind: Ist das sicher in beiden 2 und 3? Gibt es eine bessere Möglichkeit, die Kompatibilität sicherzustellen?

Hinweis: Ich verwende nicht for line in p.stdout:, da ich jede Zeile gedruckt werden muss, wie es generiert wird und this answerp.stdout einen zu großen Puffer hat.

+0

FYI. Ich habe Ihren Code in Ubuntu gegen Python 2 ausprobiert, ich bekomme: 'Hallo {1..5}' –

+1

Hinzufügen 'executable = '/ bin/bash' zum' Popen' Aufruf wird helfen, die richtige Ausgabe zu bekommen. Ich untersuche noch, warum der Fehler in Python 3. –

+0

Dank @HaiVu, lief ich es auf einem Mac und in CentOS, beide mit bash, und es funktionierte ohne Notwendigkeit, die ausführbare Datei anzugeben. Wenn ich es in Ubuntu versuche, sehe ich das gleiche wie du, keine Ahnung warum. – foglerit

Antwort

3

Sie können unversal_newlines=True hinzufügen.

p = subprocess.Popen(call, stdout=subprocess.PIPE, shell=True, universal_newlines=True) 
for line in iter(p.stdout.readline, ''): 
    print(line, end='') 

Statt bytes, str wird '' werden zurückgeführt, so wird in beiden Situationen.

Hier ist, was die Dokumentation über die Möglichkeit sagen:

Wenn universal_newlines False die Dateiobjekte stdin, stdout und stderr als Binärströme geöffnet werden, und keine Linie Umwandlung endet erfolgt .

Wenn universal_newlines True ist, werden diese Dateiobjekte geöffnet werden, wie Text Universal-Zeilenumbrüche Modus mit der Codierung von locale.getpreferredencoding (False) zurückStröme in. Für die Standardeingabe werden Zeilenendezeichen '\ n' in der Eingabe in das Standardzeilentrennzeichen os.linesep konvertiert. Für stdout und stderr werden alle Zeilenenden in der Ausgabe in '\ n' umgewandelt. Weitere Informationen finden Sie in der Dokumentation der Klasse io.TextIOWrapper, wenn das Newline-Argument für seinen Konstruktor None ist.

Es ist nicht explizit über den bytes gegen str Unterschied genannt, aber es wird angedeutet, mit der Feststellung, dass False einen binären Stream zurück und True gibt einen Textstrom.

+0

Ich hatte dieses Problem vor nur 3 Tagen in einem Programm, das ich in Python 3 konvertiert habe. – SethMMorton

+0

Danke! Dies scheint gut zu funktionieren. – foglerit

0

können Sie p.communicate() verwenden und dann entschlüsseln, wenn es ein bytes Objekt ist:

from __future__ import print_function 
import subprocess 

def b(t): 
    if isinstance(t, bytes): 
     return t.decode("utf8") 
    return t 

call = 'for i in {1..5}; do sleep 1; echo "Hello $i"; done' 
p = subprocess.Popen(call, stdout=subprocess.PIPE, shell=True) 
stdout, stderr = p.communicate() 

for line in iter(b(stdout).splitlines(), ''): 
    print(line, end='') 

Dies würde sowohl in Python arbeiten 2 und Python 3

+0

Das Problem mit 'communicate' ist, dass gewartet wird, bis der Subprozess abgeschlossen ist, bevor das stdout zurückgegeben wird. Es ist das beabsichtigte Verhalten, es ist eine gute Lösung, aber wenn der Subprozess lange läuft oder würde mehrere GB der Ausgabe generieren, kann dies nicht wünschenswert sein. – SethMMorton