Python 2到3转换:迭代子进程stdout中的行

时间:2016-05-28 14:51:41

标签: python python-3.x subprocess

我有以下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的缓冲区太大。

2 个答案:

答案 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。

没有明确提及bytesstr之间的差异,但声明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