首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >python,windows :用shlex解析命令行

python,windows :用shlex解析命令行
EN

Stack Overflow用户
提问于 2015-11-06 05:54:36
回答 1查看 9.1K关注 0票数 13

当您必须拆分命令行(例如调用Popen )时,最佳实践似乎是

subprocess.Popen(shlex.split(cmd), ...

但RTFM

shlex类使编写类似于Unix的简单语法的词法分析器变得非常容易。

那么,win32的正确方式是什么呢?那么引用解析和POSIX与非POSIX模式又如何呢?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-03-09 18:46:37

到目前为止,Python for Windows/中还没有有效的命令行拆分函数。(2016年3月)

子过程

因此,简单地说,subprocess.Popen .call等最好做如下:

代码语言:javascript
复制
if sys.platform == 'win32':
    args = cmd
else:
    args = shlex.split(cmd)
subprocess.Popen(args, ...)

上,对于shell选项的任何一个值,拆分都不是必需的,而在内部,shell只是使用subprocess.list2cmdline再次重新加入拆分参数:-)。

对于选项shell=True,在Unix上也不需要shlex.split

不管是否拆分,在Windows上启动.bat.cmd脚本(与.exe .com不同)时,您需要明确地包含文件扩展名--除非是shell=True

然而,关于命令行拆分的注释:

shlex.split(cmd, posix=0)在Windows路径中保留反斜杠,但它不理解引用和转义的正确性。尚不清楚shlex的posix=0模式对什么有好处--但是99%肯定会引诱Windows/跨平台程序员.

Windows公开ctypes.windll.shell32.CommandLineToArgvW

解析Unicode命令行字符串,并返回指向命令行参数的指针数组,以及此类参数的计数,其方式类似于标准的C运行时argc和argc值。

代码语言:javascript
复制
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解析有很弱的相似之处:

代码语言:javascript
复制
>>> win_CommandLineToArgvW('aaa"bbb""" ccc')
[u'aaa"bbb"""', u'ccc']
>>> win_CommandLineToArgvW('""  aaa"bbb""" ccc')
[u'', u'aaabbb" ccc']
>>> 
代码语言:javascript
复制
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)。它列出了已经在Windows和Linux bash上进行的严格的实壳测试,以及test_shlex的遗留posix测试模式。对剩余bug的反馈感兴趣。

代码语言:javascript
复制
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
票数 30
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33560364

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档