简体   繁体   English

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

[英]Python 2 to 3 conversion: iterating over lines in subprocess stdout

I have the following Python 2 example code that I want to make compatible with Python 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='')

This works well in Python 2 but in Python 3 p.stdout does not allow me to specify an encoding and reading it will return byte strings, rather than Unicode, so the comparison with '' will always return false and iter won't stop. 这在Python 2中运行良好,但在Python 3中, p.stdout不允许我指定编码并且读取它将返回字节字符串而不是Unicode,因此与''的比较将始终返回false并且iter将不会停止。 This issue seems to imply that in Python 3.6 there'll be a way to define this encoding. 这个问题似乎暗示在Python 3.6中有一种定义这种编码的方法。

For now, I have changed the iter call to stop when it finds an empty bytes string iter(p.stdout.readline, b'') , which seems to work in 2 and 3. My questions are: Is this safe in both 2 and 3? 现在,我已经将iter调用更改为当它找到一个空字节字符串iter(p.stdout.readline, b'')时停止,这似乎在2和3中工作。我的问题是:这两个都安全吗?和3? Is there a better way of ensuring compatibility? 有没有更好的方法来确保兼容性?

Note: I'm not using for line in p.stdout: because I need each line to be printed as it's generated and according to this answer p.stdout has a too large a buffer. 注意:我没有for line in p.stdout:使用for line in p.stdout:因为我需要在生成时打印每一行,根据这个答案 p.stdout有一个太大的缓冲区。

You can add unversal_newlines=True . 您可以添加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='')

Instead of bytes , str will be returned so '' will work in both situations. 而不是bytes ,将返回str因此''将在两种情况下都有效。

Here is what the docs have to say about the option: 以下是文档对该选项的说法:

If universal_newlines is False the file objects stdin, stdout and stderr will be opened as binary streams, and no line ending conversion is done. 如果universal_newlines为False,则文件对象stdin,stdout和stderr将作为二进制流打开,并且不会进行行结束转换。

If universal_newlines is True, these file objects will be opened as text streams in universal newlines mode using the encoding returned by locale.getpreferredencoding(False). 如果universal_newlines为True,则这些文件对象将使用locale.getpreferredencoding(False)返回的编码以通用换行模式打开为文本流。 For stdin, line ending characters '\\n' in the input will be converted to the default line separator os.linesep. 对于stdin,输入中的行结束字符'\\ n'将转换为默认行分隔符os.linesep。 For stdout and stderr, all line endings in the output will be converted to '\\n'. 对于stdout和stderr,输出中的所有行结尾都将转换为'\\ n'。 For more information see the documentation of the io.TextIOWrapper class when the newline argument to its constructor is None. 有关更多信息,请参阅io.TextIOWrapper类的文档,当其构造函数的换行参数为None时。

It's not explicitly called out about the bytes versus str difference, but it is implied by stating that False returns a binary stream and True returns a text stream. 它没有明确地提到bytesstr区别,但是通过声明False返回二进制流并且True返回文本流来暗示它。

You can use p.communicate() and then decode it if it is a bytes object: 你可以使用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='')

This would work in both Python 2 and Python 3 这适用于Python 2和Python 3

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM