Преобразование Python 2 в 3: перебор строк в подпроцессе stdout
У меня есть следующий пример кода 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
не остановится. эта проблема , по-видимому, подразумевает, что в Python 3.6 будет способ определить эту кодировку.
На данный момент я изменил вызов iter
на остановитесь, когда он найдет пустую байтовую строку iter(p.stdout.readline, b'')
, которая, кажется, работает в 2 и 3. Мои вопросы таковы: безопасно ли это и во 2, и в 3? Есть ли лучший способ обеспечить совместимость?
Примечание: я не использую for line in p.stdout:
, потому что мне нужно, чтобы каждая строка печаталась по мере ее создания и в соответствии с этим ответом p.stdout
имеет слишком большой буфер.
2 ответа:
Вы можете добавить
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, то эти файловые объекты будут открыты как текстовые потоки в режиме универсальных новых строк с использованием кодирование, возвращенное место действия.getpreferredencoding (False). Для stdin, символы окончания строки '\n ' во входных данных будет преобразован в разделитель строк по умолчанию ос.лайнесеп. Для stdout и stderr все окончания строк в выходных данных будут быть преобразованным в '\n'. Для получения дополнительной информации см. документацию Ио.TextIOWrapper класс, когда аргумент новой строки в его конструктор-это никто.
Это не вызвано явно о разнице
bytes
Противstr
, но подразумевается утверждая, чтоFalse
возвращает двоичный поток, аTrue
возвращает текстовый поток.
Вы можете использовать
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