我需要将从python调用的powershell stdout解码为python字符串 .
我的最终目标是以字符串列表的形式获取Windows中网络适配器的名称 . 我当前的功能看起来像这样,在Windows 10上使用英语时效果很好:
def get_interfaces():
ps = subprocess.Popen(['powershell', 'Get-NetAdapter', '|', 'select Name', '|', 'fl'], stdout = subprocess.PIPE)
stdout, stdin = ps.communicate(timeout = 10)
interfaces = []
for i in stdout.split(b'\r\n'):
if not i.strip():
continue
if i.find(b':')<0:
continue
name, value = [ j.strip() for j in i.split(b':') ]
if name == b'Name':
interfaces.append(value.decode('ascii')) # This fails for other users
return interfaces
其他用户使用不同的语言,因此 value.decode('ascii')
失败了 . 例如 . 一位用户报告说改为 decode('ISO 8859-2')
对他来说效果很好(所以它不是UTF-8) . 我怎么知道编码来解码通过调用powershell返回的stdout字节?
UPDATE
经过一些实验,我更加困惑 . 我的控制台中由 chcp
返回的代码页是437.我将网络适配器名称更改为包含非ascii和非cp437字符的名称 . 在交互式PowerShell中运行 Get-NetAdapter | select Name | fl
正确显示名称甚至是非cp437字符 . 当我从python中调用powershell时,非ascii字符转换为最接近的ascii字符(例如,ā到a,ž到z)和 .decode(ascii)
工作得很好 . 这种行为(以及相应的解决方案)可能依赖于Windows版本吗?我在Windows 10上,但用户可能在较旧的Windows上使用Windows 7 .
2 回答
输出字符编码可能取决于特定的命令,例如:
输出
✌(U+270C)字符已成功收到 .
子脚本的字符编码是使用PowerShell会话中的
PYTHONIOENCODING
envvar设置的 . 我已选择utf-32
作为输出编码,以便它与演示的Windows ANSI和OEM代码页不同 .请注意,父Python脚本的stdout编码是OEM代码页(在本例中为
cp437
) - 该脚本从Windows控制台运行 . 如果将父Python脚本的输出重定向到文件/管道,则Python 3中默认使用ANSI代码页(例如,cp1252
) .要解码可能包含当前OEM代码页中不可解码字符的powershell输出,您可以临时设置
[Console]::OutputEncoding
(受@eryksun's comments启发):输出
对于stdout,
fl
和tee
都使用[Console]::OutputEncoding
(默认行为就像| Write-Output
被附加到管道一样) .tee
使用utf-16将文本保存到文件中 . 输出显示✌(U+270C)已成功解码 .$OutputEncoding
用于解码管道中间的字节:输出
这是正确的:
b'\xf0\x9f\x98\x8a'.decode('utf-8') == u'\U0001f60a'
. 使用默认的$OutputEncoding
(ascii),我们会得到b'????\r\n'
.注意:
b'\n'
替换为b'\r\n'
尽管使用了二进制API,例如os.read/os.write
(msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
此处没有效果)如果输出中没有换行符,则追加
b'\r\n'
:输出:
换行符附加到管道输出 .
如果我们忽略单独的代理,那么设置
UTF8Encoding
允许通过管道传递所有Unicode字符,包括非BMP字符 . 如果配置了$env:PYTHONIOENCODING = "utf-8:ignore"
,则可以在Python中使用文本模式 .如果未重定向stdout,则使用Unicode API将字符打印到控制台 - 如果控制台(TrueType)字体支持,则可以显示任何[BMP] Unicode字符 .
这可能是由于
System.Text.InternalDecoderBestFitFallback
为[Console]::OutputEncoding
设置 - 如果Unicode字符无法在给定编码中编码,则将其传递给回退(使用最合适的char或'?'
而不是原始字符) .如果我们忽略cp65001中的错误以及更高版本中支持的新编码列表,那么行为应该是相同的 .
这是一个已被标记为wontfix的Python 2错误:https://bugs.python.org/issue19264
如果你想让它在Windows下工作,我必须使用Python 3 .