当您必须拆分命令行时,例如调用 Popen
,最佳实践似乎是
subprocess.Popen(shlex.split(cmd), ...
但是RTFM
The
shlex
class makes it easy to write lexical analyzers for simple syntaxes resembling that of the Unix shell ...
那么,在 win32 上正确的方法是什么? 引号解析和 POSIX 与非 POSIX 模式又如何呢?
最佳答案
到目前为止,Windows/多平台的 Python stdlib 中还没有有效的命令行拆分功能。 (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
,shlex.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
:
Parses a Unicode command line string and returns an array of pointers to the command line arguments, along with a count of such arguments, in a way that is similar to the standard C run-time argv and argc values.
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 库提案的以下函数。它的多平台;它比 shlex 快 10 倍,shlex 执行单字符步进和流式处理;并且还尊重与管道相关的字符(与 shlex 不同)。它列出了 Windows 和 Linux bash 上已经存在的一系列严格的真实 shell 测试,以及 test_shlex
的遗留 posix 测试模式。
对有关剩余错误的反馈感兴趣。
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
关于 python , Windows : parsing command lines with shlex,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33560364/