2015-04-03 161 views
2

以此示例代码片段为例。在Windows中处理unicode subprocess env的正确方法是什么?

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.pathdocumented是使用Unicode完全确定。处理这个(和类似的代码)的正确方法是什么,以便一切按预期工作?显而易见的解决方案是在unicode路径上调用.encode(),但我不确定这是否会导致意外行为。

+0

错误消息似乎不言自明;据推测,Python根本不支持使用Unicode环境变量创建子进程。 – 2015-04-03 08:55:26

回答

3

在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)进程启动一个Unicode进程时,你会发生什么呢?CreateProcess()?所有参数都会发生同样的情况,它们会在调用者的代码页中解码并转换为Windows版本的UCS-2宽字符。

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

所以,是的,Python的2 Unicode的环境中都或多或少的破碎。

+0

如果在Python 2中需要'CreateProcessW',请改为使用win32process,或者甚至使用ctypes。 – eryksun 2015-04-03 11:46:49

相关问题