当您必须拆分命令行时,例如 call Popen
,最佳实践似乎是
subprocess.Popen(shlex.split(cmd), ...
但RTFM
该类
shlex
使为类似于 Unix shell 的简单语法编写词法分析器变得容易...
那么,win32上的正确方法是什么?那么报价解析和 POSIX 与非 POSIX 模式呢?
当您必须拆分命令行时,例如 call Popen
,最佳实践似乎是
subprocess.Popen(shlex.split(cmd), ...
但RTFM
该类
shlex
使为类似于 Unix shell 的简单语法编写词法分析器变得容易...
那么,win32上的正确方法是什么?那么报价解析和 POSIX 与非 POSIX 模式呢?
到目前为止,Windows/多平台的 Python 标准库中还没有有效的命令行拆分功能。(2016 年 3 月)
所以简而言之subprocess.Popen .call
等。最好喜欢:
if sys.platform == 'win32':
args = cmd
else:
args = shlex.split(cmd)
subprocess.Popen(args, ...)
在 Windows 上,选项的任何一个值都不需要拆分shell
,并且在内部 Popen 只是subprocess.list2cmdline
用来再次重新加入拆分参数:-)。
使用选项shell=True
theshlex.split
在 Unix 上也不是必需的。
拆分与否,在 Windows 上用于启动.bat
或.cmd
脚本(与 .exe .com 不同)您需要明确包含文件扩展名 - 除非shell=True
.
shlex.split(cmd, posix=0)
在 Windows 路径中保留反斜杠,但它不理解正确的引用和转义。它不是很清楚 shlex 的 posix=0 模式有什么好处 - 但 99% 它肯定会吸引 Windows/跨平台程序员......
Windows API 公开ctypes.windll.shell32.CommandLineToArgvW
:
解析 Unicode 命令行字符串并返回指向命令行参数的指针数组以及此类参数的计数,其方式类似于标准 C 运行时 argv 和 argc 值。
def win_CommandLineToArgvW(cmd):
import ctypes
nargs = ctypes.c_int()
ctypes.windll.shell32.CommandLineToArgvW.restype = ctypes.POINTER(ctypes.c_wchar_p)
lpargs = ctypes.windll.shell32.CommandLineToArgvW(unicode(cmd), ctypes.byref(nargs))
args = [lpargs[i] for i in range(nargs.value)]
if ctypes.windll.kernel32.LocalFree(lpargs):
raise AssertionError
return args
然而,该功能CommandLineToArgvW
是虚假的 - 或者与强制性标准 C解析稍有相似:argv & argc
>>> win_CommandLineToArgvW('aaa"bbb""" ccc')
[u'aaa"bbb"""', u'ccc']
>>> win_CommandLineToArgvW('"" aaa"bbb""" ccc')
[u'', u'aaabbb" ccc']
>>>
C:\scratch>python -c "import sys;print(sys.argv)" aaa"bbb""" ccc
['-c', 'aaabbb"', 'ccc']
C:\scratch>python -c "import sys;print(sys.argv)" "" aaa"bbb""" ccc
['-c', '', 'aaabbb"', 'ccc']
观看http://bugs.python.org/issue1724822以了解 Python 库中未来可能添加的内容。(“fisheye3”服务器上提到的功能实际上并不正确。)
有效的 Windows 命令行拆分相当疯狂。比如试\ \\ \" \\"" \\\"aaa """"
...
我当前用于跨平台命令行拆分的候选函数是我为 Python lib 提案考虑的以下函数。它的多平台;它比执行单字符步进和流式传输的 shlex 快约 10 倍;并且还尊重与管道相关的字符(与 shlex 不同)。它列出了已经在 Windows 和 Linux bash 上进行的严格的真实 shell 测试,以及test_shlex
. 对有关剩余错误的反馈感兴趣。
def cmdline_split(s, platform='this'):
"""Multi-platform variant of shlex.split() for command-line splitting.
For use with subprocess, for argv injection etc. Using fast REGEX.
platform: 'this' = auto from current platform;
1 = POSIX;
0 = Windows/CMD
(other values reserved)
"""
if platform == 'this':
platform = (sys.platform != 'win32')
if platform == 1:
RE_CMD_LEX = r'''"((?:\\["\\]|[^"])*)"|'([^']*)'|(\\.)|(&&?|\|\|?|\d?\>|[<])|([^\s'"\\&|<>]+)|(\s+)|(.)'''
elif platform == 0:
RE_CMD_LEX = r'''"((?:""|\\["\\]|[^"])*)"?()|(\\\\(?=\\*")|\\")|(&&?|\|\|?|\d?>|[<])|([^\s"&|<>]+)|(\s+)|(.)'''
else:
raise AssertionError('unkown platform %r' % platform)
args = []
accu = None # collects pieces of one arg
for qs, qss, esc, pipe, word, white, fail in re.findall(RE_CMD_LEX, s):
if word:
pass # most frequent
elif esc:
word = esc[1]
elif white or pipe:
if accu is not None:
args.append(accu)
if pipe:
args.append(pipe)
accu = None
continue
elif fail:
raise ValueError("invalid or incomplete shell string")
elif qs:
word = qs.replace('\\"', '"').replace('\\\\', '\\')
if platform == 0:
word = word.replace('""', '"')
else:
word = qss # may be even empty; must be last
accu = (accu or '') + word
if accu is not None:
args.append(accu)
return args