在Windows中处理unicode子进程env的正确方法是什么?

时间:2015-04-03 08:46:35

标签: python windows python-2.7

以此示例摘录。

import subprocess
import os

env = os.environ.copy()
env["FOO"] = u"foo"
subprocess.check_call(["ls", "-l"], env=env)

在Windows上,此操作失败。

C:\Python27\python.exe test.py
Traceback (most recent call last):
  File "test.py", line 7, in <module>
    subprocess.check_call(["ls", "-l"], env=env)
  File "C:\Python27\lib\subprocess.py", line 535, in check_call
    retcode = call(*popenargs, **kwargs)
  File "C:\Python27\lib\subprocess.py", line 522, in call
    return Popen(*popenargs, **kwargs).wait()
  File "C:\Python27\lib\subprocess.py", line 710, in __init__
    errread, errwrite)
  File "C:\Python27\lib\subprocess.py", line 958, in _execute_child
    startupinfo)
TypeError: environment can only contain strings

sys.path documented与unicode完全正常。处理这个(和类似代码)的正确方法是什么,以便一切按预期工作?显而易见的解决方案是在unicode路径上调用.encode(),但我不确定这是否会导致意外行为。

1 个答案:

答案 0 :(得分:4)

在Windows上,将环境字典传递给subprocess.check_call()归结为将环境传递给CreateProcess()。那个人实际上可以使用unicode字符串(在CreateProcessW()化身)。

然而,从python 2.7的 _subprocess.c

/* TODO: handle unicode command lines? */
/* TODO: handle unicode environment? */

所以你不是第一个想到这个问题的人。

您的问题也没有通用的解决方案,因为环境由被调用的进程解释,其中一些也由系统或系统库自动解释。因此正确的编码取决于目标进程所期望的内容。

不幸的是,虽然Windows上的Python 2确实处理了Unicode,但它实际上将零终止的窄字符串(即PyString_AS_STRING()返回char *)传递给系统函数。

现在,Windows本身如何处理两个不同版本的环境变量,因为很明显,它似乎可以传递宽或窄的环境字符串。

目标进程只能访问GetEnvironmentStrings(),它返回宽字符或窄字符,具体取决于应用程序是使用Unicode支持编译还是不支持。

那么当您从狭窄的(ANSI)进程执行CreateProcess()以启动Unicode进程时会发生什么?与所有参数相同的事情,它们在调用者的代码页中被解码并转换为Windows版本的UCS-2宽字符。

所以正确的方法可能是使用系统代码页,因为只有这样才能在unicode目标进程中正确显示字符串。这当然会阻止您使用不在该代码页中的字符......

所以,是的,Python 2上的Unicode环境或多或少都会被破坏。