Warum gibt Popen.communicate () b'hi \ n 'anstelle von' hi 'zurück?

92

Kann jemand erklären, warum dem gewünschten Ergebnis "Hallo" ein Buchstabe "b" und ein Zeilenumbruch vorangestellt sind?

Ich benutze Python 3.3

>>> import subprocess
>>> print(subprocess.Popen("echo hi", shell=True,
                           stdout=subprocess.PIPE).communicate()[0])
b'hi\n'

Dieses zusätzliche 'b' wird nicht angezeigt, wenn ich es mit Python 2.7 ausführe

imaginerThat
quelle
1
Welche Version von Python verwenden Sie?
Necrolyte2
2
Ich bin mir nicht sicher über das 'b', aber der Zeilenumbruch liegt daran, dass echo higedruckt wird hi\r\n. Um dies zu vermeiden, können Sie am Ende .strip () oder einen ähnlichen Fix hinzufügen.
Azhrei
7
Sie könnten check_output()anstelle von .communicate()hier verwenden:print(subprocess.check_output("echo hi", shell=True, universal_newlines=True), end="")
jfs

Antworten:

22

Der Befehl echo gibt standardmäßig ein Zeilenumbruchzeichen zurück

Vergleichen Sie damit:

print(subprocess.Popen("echo -n hi", \
    shell=True, stdout=subprocess.PIPE).communicate()[0])

Das b vor der Zeichenfolge gibt an, dass es sich um eine Bytefolge handelt, die einer normalen Zeichenfolge in Python 2.6+ entspricht

http://docs.python.org/3/reference/lexical_analysis.html#literals

Nekrolyt2
quelle
5
Sie brauchen kein '\' in den Klammern.
JFS
94

Das bgibt an, dass Sie byteseine binäre Folge von Bytes und keine Folge von Unicode-Zeichen haben. Unterprozesse geben Ausgabebytes und keine Zeichen aus. Das ist also, was communicate()zurückkehrt.

Der bytesTyp ist nicht direkt in der print()Lage, daher wird Ihnen der Typ angezeigt, reprden bytesSie haben. Wenn Sie die Codierung der Bytes kennen, die Sie vom Unterprozess erhalten haben, können Sie decode()sie in eine druckbare konvertieren str:

>>> print(b'hi\n'.decode('ascii'))
hi

Dieses spezielle Beispiel funktioniert natürlich nur, wenn Sie tatsächlich ASCII vom Unterprozess empfangen. Wenn es nicht ASCII ist, erhalten Sie eine Ausnahme:

>>> print(b'\xff'.decode('ascii'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xff in position 0…

Die Newline ist Teil dessen, was echo hiausgegeben wurde. echoDie Aufgabe besteht darin, die Parameter auszugeben, die Sie übergeben, gefolgt von einer neuen Zeile. Wenn Sie nicht an Leerzeichen interessiert sind, die die Prozessausgabe umgeben, können Sie Folgendes verwenden strip():

>>> b'hi\n'.strip()
b'hi'
Zickzack
quelle
1
Wie bringt man die Funktion print () dazu, eine Byte-Zeichenfolge ohne vorangestelltes 'b' zu drucken? Oder müssen Sie es zuerst in eine Unicode-Zeichenfolge konvertieren?
ImagineerThat
Ich bin gespannt, os.popenob es bei der Rückgabe von Textzeichenfolgen eine Möglichkeit gibt, subprocess.Popendiese anstelle der Bytezeichenfolgen auch zurückzugeben.
Pavel Šimerda
11
Ich antworte mir selbst, es gibt eine Option mit einem kryptischen Namen universal_newlines, die bewirkt, dass das PopenObjekt Textzeichenfolgen akzeptiert und zurückgibt.
Pavel Šimerda
3
@ PavelŠimerda Während os.popen Textzeichenfolgen zurückgibt, werden diese offenbar zumindest unter Windows für Nicht-ASCII-Zeichen falsch dekodiert. Das Ausführen check_output("dir"), Extrahieren eines Dateinamens aus der Ausgabe und der anschließende Versuch, darauf zuzugreifen, openschlägt fehl, wenn der Dateiname deutsche Umlaute enthält. Könnte ein Fehler sein.
kdb
56

Wie bereits erwähnt, echo hikehrt tatsächlich zurück hi\n, was ein erwartetes Verhalten ist.

Aber Sie möchten wahrscheinlich nur die Daten in einem "richtigen" Format erhalten und sich nicht mit der Codierung befassen. Alles, was Sie tun müssen, ist die universal_newlines=TrueOption zu übergeben, um dies zu subprocess.Popen()mögen:

>>> import subprocess
>>> print(subprocess.Popen("echo hi",
                           shell=True,
                           stdout=subprocess.PIPE,
                           universal_newlines=True).communicate()[0])
hi

Auf diese Weise Popen()werden diese unerwünschten Symbole von selbst ersetzt.

Danil
quelle
11
universal_newlines=TrueLief wie am Schnürchen. Dies sollte meiner bescheidenen Meinung nach die akzeptierte Antwort sein ...
Ethan Strider
3
Es werden zusätzliche Leerzeilen erzeugt.
LoMaPh
1
Möglicherweise benötigen Sie sowohl universal_newlines=True in Popen(um das zu entfernen b'') als auch a strip()in der resultierenden Zeichenfolge, wenn Sie die abschließende Newline hacken möchten.
Arielf
Zu Ihrer Information, die Dokumentation sagt, universal_newlinesist jetzt nur ein abwärtskompatibler Alias ​​für den textParameter, der klarer ist, aber nur in Python 3.7 und höher.
Harry Cutts
Es werden zusätzliche Leerzeilen erzeugt, da dies nicht funktioniert. universal_newlines entfernt nicht \ n
kol23
8

b ist die Bytedarstellung und \ n ist das Ergebnis der Echoausgabe.

Im Folgenden werden nur die Ergebnisdaten gedruckt

import subprocess
print(subprocess.Popen("echo hi", shell=True,stdout=subprocess.PIPE).communicate()[0].decode('utf-8').strip())
Jenish
quelle