首页 文章

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

提问于
浏览
3

我有以下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中有效 . 我的问题是:这在2和3中都是安全的吗?有没有更好的方法来确保兼容性?

注意:我没有使用 for line in p.stdout: 因为我需要在生成时打印每一行,并且根据this answer p.stdout 有一个太大的缓冲区 .

2 回答

  • 4

    您可以添加 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(False)返回的编码以通用换行模式打开为文本流 . 对于stdin,输入中的行结束字符'\ n'将转换为默认行分隔符os.linesep . 对于stdout和stderr,输出中的所有行结尾都将转换为'\ n' . 有关更多信息,请参阅io.TextIOWrapper类的文档,当其构造函数的换行参数为None时 .

    没有明确地提到 bytesstr 之间的区别,但是通过声明 False 返回二进制流并且 True 返回文本流来暗示它 .

  • 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

相关问题