为什么Popen.communicate()返回b'hi\n'而不是'hi'?

130

有人可以解释一下为什么我想要的结果“hi”前面有一个字母'b',后面还有一个换行符吗?

我正在使用Python 3.3

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

如果我使用 Python 2.7 运行,这个额外的 'b' 并不会出现。


1
你正在使用哪个版本的Python? - Necrolyte2
2
不确定'b'是什么意思,但换行符是因为echo hi会打印出hi\r\n。为了避免这种情况,你可以在末尾添加.strip()或类似的修复方法。 - azhrei
8
你可以在这里使用check_output()代替.communicate()print(subprocess.check_output("echo hi", shell=True, universal_newlines=True), end="") - jfs
4个回答

121

b表示你拥有的是bytes,它是一系列字节的二进制序列,而不是Unicode字符的字符串。子进程输出的是字节而不是字符,这就是communicate()返回的内容。

bytes类型不能直接被print()输出,因此你看到的是bytesrepr。如果你知道从子进程接收到的字节的编码方式,你可以使用decode()将它们转换为可打印的str

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

当然,这个具体的例子只适用于从子进程接收ASCII码的情况。如果不是ASCII码,你将会得到一个异常:
>>> 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…

换行符是 echo hi 输出的一部分。 echo 的工作是输出您传递给它的参数,后跟一个换行符。 如果您不关心进程输出周围的空格,则可以使用 strip(),如下所示:
>>> b'hi\n'.strip()
b'hi'

1
你如何让print()函数在不带有前缀“b”的情况下打印字节字符串?或者你需要先将其转换为Unicode字符串吗? - imagineerThat
我很好奇,当os.popen返回文本字符串时,是否有办法使subprocess.Popen也返回它们,而不是字节字符串。 - Pavel Šimerda
14
我来回答,有一个名字很神秘的选项叫做“universal_newlines”,它会使得Popen对象接受和返回文本字符串。 - Pavel Šimerda
3
在Windows系统中,os.popen返回的文本字符串中的非ASCII字符似乎被解码错误。例如,运行check_output("dir"),从输出中提取文件名,然后尝试使用open访问该文件将失败,如果文件名包含德语umlauts。这可能是一个错误。建议修复。 - kdb

99

如之前提到的,echo hi 实际上会返回 hi\n,这是一种预期的行为。

但你可能只想以“正确”的格式获取数据,而不需要处理编码。你只需向 subprocess.Popen() 传递 universal_newlines=True 选项即可:

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

这样,Popen() 将自行替换这些不需要的符号。


20
universal_newlines=True起到了神奇的作用。依我之见,这应该成为被采纳的答案。 - Ethan Strider
5
它会产生多余的空行。 - LoMaPh
7
如果您想剪掉行末的换行符,则在 Popen 中需要使用 universal_newlines=True(以消除 b'')并在结果字符串上使用 strip() - arielf
1
FYI,文档 表示 universal_newlines 现在只是 text 参数的向后兼容别名,这更清晰,但仅适用于 Python 3.7 及以上版本。 - Harry Cutts
它产生了额外的空行,因为它不起作用。universal_newlines不能删除\n。 - kol23
显示剩余3条评论

28

默认情况下,echo命令会返回一个换行符。

与此进行比较:

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

关于字符串前面的b,它表示它是一个字节序列,在Python 2.6+中相当于普通字符串。

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


10
括号中不需要加反斜杠符号“\”。 - jfs

12
b是字节表示,\n是输出内容的结果。

以下代码将仅打印输出数据。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接