¿Por qué Popen.communicate () devuelve b'hi \ n 'en lugar de' hola '?

92

¿Alguien puede explicar por qué el resultado que quiero, "hola", está precedido por una letra "b" y seguido de una nueva línea?

Estoy usando Python 3.3

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

Esta 'b' adicional no aparece si la ejecuto con Python 2.7

Imaginador que
fuente
1
¿Qué versión de Python estás usando?
Necrolyte2
2
No estoy seguro de la 'b', pero la nueva línea se debe a que echo hiimprime hi\r\n. Para evitar eso, puede agregar .strip () al final, o una solución similar.
azhrei
7
podría usar en check_output()lugar de .communicate()aquí:print(subprocess.check_output("echo hi", shell=True, universal_newlines=True), end="")
jfs

Respuestas:

22

El comando echo por defecto devuelve un carácter de nueva línea

Compare con esto:

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

En cuanto a la b que precede a la cadena, indica que es una secuencia de bytes que es equivalente a una cadena normal en Python 2.6+

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

Necrólito2
fuente
5
no necesita '\' entre paréntesis.
jfs
94

El bindica que lo que tiene es bytes, que es una secuencia binaria de bytes en lugar de una cadena de caracteres Unicode. Subprocesa bytes de salida, no caracteres, así que eso communicate()es lo que está regresando.

El bytestipo no es directamente print()capaz, por lo que se le muestra reprel bytesque tiene. Si conoce la codificación de los bytes que recibió del subproceso, puede usarlos decode()para convertirlos en imprimibles str:

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

Por supuesto, este ejemplo específico solo funciona si realmente está recibiendo ASCII del subproceso. Si no es ASCII, obtendrá una excepción:

>>> 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…

La nueva línea es parte de lo que echo hitiene salida. echoEl trabajo es generar los parámetros que le pasas, seguidos de una nueva línea. Si no está interesado en los espacios en blanco que rodean la salida del proceso, puede usar strip()así:

>>> b'hi\n'.strip()
b'hi'
zigg
fuente
1
¿Cómo se consigue que la función print () imprima una cadena de bytes sin una 'b' precedida? ¿O primero necesita convertirlo en una cadena Unicode?
imagineerThat
Tengo curiosidad, cuando os.popendevuelve cadenas de texto, si hay una manera de hacer que subprocess.Popentambién las devuelva, en lugar de las cadenas de bytes.
Pavel Šimerda
11
Me responderé, hay una opción con un nombre críptico universal_newlinesque hace que el Popenobjeto acepte y devuelva cadenas de texto.
Pavel Šimerda
3
@ PavelŠimerda Si bien os.popen devuelve cadenas de texto, aparentemente se están decodificando incorrectamente para caracteres que no son ascii, al menos en Windows. Por ejemplo check_output("dir"), ejecutar , extraer un nombre de archivo de la salida y luego intentar acceder a él openfallará si el nombre del archivo contiene diéresis en alemán. Podría ser un error.
kdb
57

Como se mencionó anteriormente, en echo hirealidad regresa hi\n, lo cual es un comportamiento esperado.

Pero probablemente desee obtener los datos en un formato "correcto" y no ocuparse de la codificación. Todo lo que necesitas hacer es pasar la universal_newlines=Trueopción para que te subprocess.Popen()guste:

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

De esta manera Popen()reemplazará estos símbolos no deseados por sí solo.

Danil
fuente
11
universal_newlines=Truetrabajado como un encanto. Esta debería ser la respuesta aceptada, en mi humilde opinión ...
Ethan Strider
3
Produce líneas extra vacías.
LoMaPh
1
Es posible que necesite tanto universal_newlines=True in Popen(para deshacerse de b'') como a strip()en la cadena resultante, si desea cortar la nueva línea de terminación.
arielf
Para su información, la documentación dice universal_newlinesque ahora es solo un alias compatible con versiones anteriores para el textparámetro, que es más claro pero solo en Python 3.7 y superior.
Harry Cutts
Produce líneas extra vacías porque no funciona. universal_newlines no elimina \ n
kol23
8

b es la representación de bytes y \ n es el resultado de la salida de eco.

Lo siguiente imprimirá solo los datos del resultado

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