Pourquoi Popen.communicate () renvoie b'hi \ n 'au lieu de' hi '?

92

Quelqu'un peut-il expliquer pourquoi le résultat que je veux, «salut», est précédé d'une lettre «b» et suivi d'une nouvelle ligne?

J'utilise Python 3.3

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

Ce 'b' supplémentaire n'apparaît pas si je l'exécute avec python 2.7

imaginateur
la source
1
Quelle version de Python utilisez-vous?
Necrolyte2
2
Pas sûr du 'b', mais la nouvelle ligne est parce que echo hiimprime hi\r\n. Pour éviter cela, vous pouvez ajouter .strip () à la fin, ou un correctif similaire.
azhrei
7
vous pouvez utiliser à la check_output()place d' .communicate()ici:print(subprocess.check_output("echo hi", shell=True, universal_newlines=True), end="")
jfs

Réponses:

22

La commande echo renvoie par défaut un caractère de nouvelle ligne

Comparez avec ceci:

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

Quant au b précédant la chaîne, il indique qu'il s'agit d'une séquence d'octets qui équivaut à une chaîne normale en Python 2.6+

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

Nécrolyte2
la source
5
vous n'avez pas besoin de '\' entre parenthèses.
jfs
94

Le bindique que ce que vous avez est bytesune séquence binaire d'octets plutôt qu'une chaîne de caractères Unicode. Les sous-processus produisent des octets, pas des caractères, c'est donc ce qui communicate()revient.

Le bytestype n'est pas directement print()capable, donc on vous montre le reprque bytesvous avez. Si vous connaissez le codage des octets que vous avez reçus du sous-processus, vous pouvez utiliser decode()pour les convertir en un imprimable str:

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

Bien sûr, cet exemple spécifique ne fonctionne que si vous recevez réellement l'ASCII du sous-processus. Si ce n'est pas ASCII, vous obtiendrez une exception:

>>> 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 nouvelle ligne fait partie de ce qui echo hia une sortie. echoLe travail de ce dernier est de sortir les paramètres que vous lui transmettez, suivis d 'une nouvelle ligne. Si vous n'êtes pas intéressé par les espaces autour de la sortie du processus, vous pouvez utiliser strip()comme ceci:

>>> b'hi\n'.strip()
b'hi'
zigg
la source
1
Comment obtenir la fonction print () pour imprimer une chaîne d'octets sans un 'b' précédent? Ou avez-vous d'abord besoin de le convertir en chaîne Unicode?
imagineerQue
Je suis curieux, lorsque os.popenrenvoie des chaînes de texte, s'il existe un moyen de les rendre subprocess.Popenégalement, au lieu des chaînes d'octets.
Pavel Šimerda
11
Je vais me répondre, il existe une option avec un nom cryptique appelé universal_newlinesqui fait que l' Popenobjet accepte et renvoie des chaînes de texte.
Pavel Šimerda
3
@ PavelŠimerda Alors que os.popen renvoie des chaînes de texte, elles sont apparemment mal décodées pour les caractères non ascii, du moins sous Windows. Par exemple, l'exécution check_output("dir"), l'extraction d'un nom de fichier de la sortie puis la tentative d'accès avec openéchouera si le nom de fichier contient des trémas allemands. Ça pourrait être un bug.
kdb
57

Comme mentionné précédemment, echo hirenvoie effectivement hi\n, ce qui est un comportement attendu.

Mais vous voulez probablement simplement obtenir les données dans un «bon» format et ne pas vous occuper du codage. Tout ce que vous avez à faire est de passer l' universal_newlines=Trueoption pour subprocess.Popen()aimer ainsi:

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

De cette manière Popen(), ces symboles indésirables seront remplacés par eux-mêmes.

Danil
la source
11
universal_newlines=Truea fonctionné comme un charme. Cela devrait être la réponse acceptée, à mon humble avis ...
Ethan Strider
3
Cela produit des lignes vides supplémentaires.
LoMaPh
1
Vous devrez peut-être à la fois universal_newlines=True in Popen(pour vous débarrasser de b'') et a strip()sur la chaîne résultante, si vous souhaitez couper la nouvelle ligne de fin.
arielf le
Pour info, la documentation indique qu'il ne universal_newliness'agit maintenant que d'un alias rétrocompatible pour le textparamètre, ce qui est plus clair mais uniquement en Python 3.7 et supérieur.
Harry Cutts
Cela produit des lignes vides supplémentaires car cela ne fonctionne pas. universal_newlines ne supprime pas \ n
kol23
8

b est la représentation en octets et \ n est le résultat de la sortie d'écho.

Ce qui suit n'imprimera que les données de résultat

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