Por que Popen.communicate () retorna b'hi \ n 'em vez de' hi '?

95

Alguém pode explicar por que o resultado que desejo, "oi", é precedido pela letra 'b' e seguido por uma nova linha?

Estou usando Python 3.3

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

Este 'b' extra não aparece se eu executá-lo com o python 2.7

imagineerThat
fonte
1
Qual versão do Python você está usando?
Necrólito 2
2
Não tenho certeza sobre o 'b', mas a nova linha é porque echo hiimprime hi\r\n. Para evitar isso, você pode adicionar .strip () no final, ou uma correção semelhante.
azhrei
7
você poderia usar em check_output()vez de .communicate()aqui:print(subprocess.check_output("echo hi", shell=True, universal_newlines=True), end="")
jfs

Respostas:

22

O comando echo por padrão retorna um caractere de nova linha

Compare com isto:

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

Quanto ao b que precede a string, ele indica que é uma sequência de bytes que é equivalente a uma string normal em Python 2.6+

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

Necrólito 2
fonte
6
você não precisa de '\' dentro dos parênteses.
jfs de
95

O bindica que o que você tem é bytesuma sequência binária de bytes, em vez de uma string de caracteres Unicode. Subprocessa bytes de saída, não caracteres, então é isso que communicate()está retornando.

O bytestipo não print()pode ser diretamente ativado, então você está sendo mostrado o reprde que bytesvocê tem. Se você souber a codificação dos bytes recebidos do subprocesso, poderá usá decode()-los para convertê-los em um imprimível str:

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

É claro que este exemplo específico só funciona se você realmente estiver recebendo ASCII do subprocesso. Se não for ASCII, você receberá uma exceção:

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

A nova linha é parte do que echo hitem saída. echoO trabalho de é produzir os parâmetros que você passa, seguido por uma nova linha. Se não estiver interessado em espaços em branco ao redor da saída do processo, você pode usar da seguinte strip()forma:

>>> b'hi\n'.strip()
b'hi'
zigg
fonte
1
Como você faz com que a função print () imprima uma string de bytes sem um 'b' precedente? Ou você precisa primeiro convertê-lo em uma string Unicode?
imagineerThat
Estou curioso, quando os.popenretorna strings de texto, se há uma maneira de fazer subprocess.Popentambém retorná-los, em vez de strings de byte.
Pavel Šimerda
11
Vou responder a mim mesmo, há uma opção com um nome críptico chamado universal_newlinesque faz com que o Popenobjeto aceite e retorne strings de texto.
Pavel Šimerda
3
@ PavelŠimerda Embora os.popen retorne strings de texto, eles aparentemente estão sendo decodificados incorretamente para caracteres não ASCII, pelo menos no Windows. Por exemplo check_output("dir"), executar , extrair um nome de arquivo da saída e tentar acessá-lo com openfalhará se o nome do arquivo contiver tremas alemães. Pode ser um bug.
kdb
61

Conforme mencionado antes, echo hirealmente retorna hi\n, o que é um comportamento esperado.

Mas provavelmente você deseja apenas obter os dados em um formato "correto" e não lidar com a codificação. Tudo que você precisa fazer é passar a universal_newlines=Trueopção de subprocess.Popen()gostar assim:

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

Desta forma Popen(), substituirá esses símbolos indesejados por si só.

Danil
fonte
11
universal_newlines=Truefuncionou como um encanto. Esta deve ser a resposta aceita, na minha humilde opinião ...
Ethan Strider
3
Produz linhas vazias extras.
LoMaPh
1
Você pode precisar tanto universal_newlines=True em Popen(para se livrar do b'') e um strip()na cadeia resultante, se você quiser cortar a nova linha de terminação.
arielf
Para sua informação, a documentação diz que universal_newlinesagora é apenas um alias compatível com versões anteriores para o textparâmetro, que é mais claro, mas apenas no Python 3.7 e superior.
Harry Cutts
Ele produz linhas vazias extras porque não funciona. universal_newlines não remove \ n
kol23
8

b é a representação de byte e \ n é o resultado da saída de eco.

A seguir irá imprimir apenas os dados do resultado

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