Страницы

Поиск по вопросам

понедельник, 24 февраля 2020 г.

Byte при печати вывода внешней команды

#python #windows #python_3x #unicode #subprocess


Для лабораторки использую кусок кода:

import socket, sys, os, subprocess

ipconfig_res = subprocess.Popen("ipconfig", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in ipconfig_res.stdout.readlines():
    print (line)


При запуске в 3.5 выдается список с содержимым типа bytes, преобразовать его в str
не получается. Как получить результат без мусора?  


    


Ответы

Ответ 1



Это не мусор, а байтовое представление строк, их нужно кодировать, чтобы получить человеко-читаемый вид: ipconfig_res = subprocess.Popen("ipconfig", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) for line in ipconfig_res.stdout.readlines(): line = line.strip() if line: print(line.decode('cp866')) Консоль: Настройка протокола IP для Windows Ethernet adapter VPN - VPN Client: Состояние среды. . . . . . . . : Среда передачи недоступна. DNS-суффикс подключения . . . . . : Ethernet adapter Подключение по локальной сети 4: Состояние среды. . . . . . . . : Среда передачи недоступна. ... Еще пример: >>> b'\x8d\xa0\xe1\xe2'.decode('cp866') 'Наст' Исправленный, по советам jfs, код: from subprocess import Popen, PIPE if __name__ == '__main__': ipconfig_res = Popen("ipconfig", universal_newlines=True, stdout=PIPE) for line in ipconfig_res.stdout: print(line, end='')

Ответ 2



subprocess модуль использует байты для обмена данными с дочерним процессом. b'\r\n' это текстовое представление (repr(bytes_object)) двоичных данных, представленных типом bytes в Питоне -- объект, представляющий последовательность двух байт (0x0d, 0x0a) в этом примере. Обычно текстовое представление показывается в интерактивной консоле Питона, где sys.displayhook использует repr(), чтобы показать объекты по умолчанию (полезно для отладки) или если вы пытаетесь напечатать байты, используя текстовый интерфейс (например, print(bytes_object)) -- нужно текст передавать вместо этого. Как получить текст из вывода внешней команды Чтобы получить текст из байт, их необходимо декодировать в Юникод, используя зависящую от команды, её опций и окружения кодировку: text = subprocess.check_output("ipconfig", encoding=encoding) До Python 3.6, Popen() не принимал encoding параметр, поэтому чтобы произвольную кодировку передать, можно явно io.TextIOWrapper() создать. Чтобы построчно вывод прочитать: #!/usr/bin/env python3 import io from subprocess import Popen, PIPE, STDOUT with Popen("ipconfig", stdout=PIPE, stderr=STDOUT, bufsize=1) as process: for line in io.TextIOWrapper(process.stdout, encoding=encoding): # здесь с line можно работать print(line, end='') Если передать universal_newlines=True в Popen(), то TextIOWrapper() используется неявно и соответственно locale.getpreferredencoding(False) кодировка используется, чтобы декодировать байты в текст. Дополнительно, окончания строк нормализируются, например, '\r\n' на входе преобразуется в '\n'. Обычно ipconfig расположен в одном из стандартных путей, просматриваемых Popen() в поиске исполняемого файла и ipconfig не является внутренней командой в cmd.exe, поэтому shell=True не нужно использовать. Как определить кодировку, чтобы вывод команды декодировать Не нужно жёстко прошивать кодировку окружения, в котором исполняется ваш Питон-скрипт. Иначе получатся кракозябы, если вы запустите команду в окружении, которое может использовать другую несовместимую кодировку символов. См.: Возвращаемая строка содержит нечитаемые символы Проблемы с кодировкой Python 2.7 Используемая кодировка для вывода зависит от команды, её опций и текущего окружения, например, Питон 3 использует encoding=locale.getpreferredencoding(False) по умолчанию -- что-нибудь вроде cp1251 на русской Винде. cmd.exe по умолчанию использует OEM code page (см. вывод chcp команды) -- что-нибудь вроде cp866. Если родительский процесс подключён к той же консоле и chcp не был использован для смены кодировки, то можно использовать encoding=os.device_encoding(1), который вызывает GetConsoleOutputCP() на Windows (это значение может отличаться от encoding=ctypes.windll.kernel32.GetOEMCP()). cmd /U использует utf-16 кодировку. Наиболее вероятно, что вывод ipconfig можно декодировать, используя encoding=os.device_encoding(1) or ctypes.windll.kernel32.GetOEMCP() в этом случае (первое не None значение).

Комментариев нет:

Отправить комментарий