我有以下Python 2示例代码,我想与Python 3兼容:
call = 'for i in {1..5}; do sleep 1; echo "Hello $i"; done'
p = subprocess.Popen(call, stdout=subprocess.PIPE, shell=True)
for line in iter(p.stdout.readline, ''):
print(line, end='')
这在Python 2中运行良好但在Python 3中p.stdout
不允许我指定编码并且读取它将返回字节字符串而不是Unicode,因此与''
的比较将始终返回false和iter
不会停止。 This issue似乎暗示在Python 3.6中有一种定义此编码的方法。
现在,我已经将iter
调用更改为在找到空字节字符串iter(p.stdout.readline, b'')
时停止,这似乎在2和3中有效。我的问题是:这两个都安全吗?和3?有没有更好的方法来确保兼容性?
注意:我没有使用for line in p.stdout:
,因为我需要在生成时打印每一行,并且this answer p.stdout
的缓冲区太大。
答案 0 :(得分:5)
您可以添加unversal_newlines=True
。
p = subprocess.Popen(call, stdout=subprocess.PIPE, shell=True, universal_newlines=True)
for line in iter(p.stdout.readline, ''):
print(line, end='')
将返回bytes
,而不是str
,因此''
将在两种情况下都有效。
以下是文档对该选项的说法:
如果universal_newlines为False,则文件对象为stdin,stdout和 stderr将作为二进制流打开,并且没有行结束转换 完了。
如果universal_newlines为True,则将打开这些文件对象 使用返回的编码以通用换行模式传输文本流 是locale.getpreferredencoding(假)。对于stdin,行结束字符 ' \ n'在输入中将转换为默认行分隔符 os.linesep。对于stdout和stderr,输出中的所有行结尾都将 转换为' \ n'。有关更多信息,请参阅文档 io.TextIOWrapper类当newline参数为其时 构造函数是None。
没有明确提及bytes
与str
之间的差异,但声明False
返回二进制流并且True
返回时隐含了这一点。文本流。
答案 1 :(得分:0)
您可以使用p.communicate()
然后解码它,如果它是bytes
对象:
from __future__ import print_function
import subprocess
def b(t):
if isinstance(t, bytes):
return t.decode("utf8")
return t
call = 'for i in {1..5}; do sleep 1; echo "Hello $i"; done'
p = subprocess.Popen(call, stdout=subprocess.PIPE, shell=True)
stdout, stderr = p.communicate()
for line in iter(b(stdout).splitlines(), ''):
print(line, end='')
这适用于Python 2和Python 3