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

Posted

技术标签:

【中文标题】为啥 Popen.communicate() 返回 b\'hi\\n\' 而不是 \'hi\'?【英文标题】:Why does Popen.communicate() return b'hi\n' instead of 'hi'?为什么 Popen.communicate() 返回 b'hi\n' 而不是 'hi'? 【发布时间】:2013-02-28 17:34:44 【问题描述】:

有人能解释一下为什么我想要的结果“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”

【问题讨论】:

你用的是什么版本的 Python? 不确定'b',但换行是因为echo hi 打印hi\r\n。为避免这种情况,您可以在末尾添加 .strip() 或类似的修复。 你可以在这里使用check_output() 而不是.communicate()print(subprocess.check_output("echo hi", shell=True, universal_newlines=True), end="") 【参考方案1】:

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

bytes 类型不直接是 print()able,因此您看到的是 reprbytes。如果您知道从子进程收到的字节的编码,则可以使用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'

【讨论】:

如何让 print() 函数打印一个没有前面的 'b' 的字节字符串?还是需要先转成unicode字符串? 我很好奇,当os.popen 返回文本字符串时,是否有办法让subprocess.Popen 也返回它们,而不是字节字符串。 我会回答自己,有一个名为 universal_newlines 的隐蔽名称选项会导致 Popen 对象接受并返回文本字符串。 @PavelŠimerda 虽然 os.popen 返回文本字符串,但它们显然被错误地解码为非 ascii 字符,至少在 Windows 上是这样。例如。运行check_output("dir"),如果文件名包含德语变音符号,则从输出中提取文件名然后尝试使用open 访问它会失败。可能是一个错误。【参考方案2】:

如前所述,echo hi 实际上确实返回了hi\n,这是预期的行为。

但您可能只想以“正确”的格式获取数据,而不是处理编码。您需要做的就是将universal_newlines=True 选项传递给subprocess.Popen(),如下所示:

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

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

【讨论】:

universal_newlines=True 就像一个魅力。以我的拙见,这应该是公认的答案... 它会产生额外的空行。 你可能需要 both universal_newlines=True in Popen (摆脱b'')和结果字符串上的strip(),如果你想砍掉终止的换行符。 仅供参考,the documentation 说 universal_newlines 现在只是 text 参数的向后兼容别名,它更清晰但仅在 Python 3.7 及更高版本中。 它会产生额外的空行,因为它不起作用。 Universal_newlines 不删除\n【参考方案3】:

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

【讨论】:

括号内不需要'\'。【参考方案4】:

b 是字节表示,\n 是 echo 输出的结果。

以下将只打印结果数据

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

【讨论】:

以上是关于为啥 Popen.communicate() 返回 b'hi\n' 而不是 'hi'?的主要内容,如果未能解决你的问题,请参考以下文章

Python Popen communicate 和wait使用上的区别

如何使用子进程 Popen.communicate() 方法?

了解 Popen.communicate

带有标准输入的 subprocess.Popen.communicate() 的管道损坏

python popen.communicate() 与多个标准输入写入

当 Popen.communicate() 还不够?