【问题标题】:python, windows : parsing command lines with shlexpython, windows : 用 shlex 解析命令行
【发布时间】:2016-02-07 05:17:44
【问题描述】:

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

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

但是RTFM

shlex 类使得为类似于 Unix shell 的简单语法编写词法分析器变得容易...

那么,win32上的正确方法是什么? 那么引号解析和 POSIX 与非 POSIX 模式呢?

【问题讨论】:

    标签: python windows command-line command-line-arguments shlex


    【解决方案1】:

    到目前为止,Python stdlib for Windows/multi-platform 中还没有有效的命令行拆分功能。 (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=Trueshlex.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 的遗留 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
    

    【讨论】:

    • 能否添加对 cme​​ts 的支持?您是否在邮件列表中提交了 PEP 或至少一封邮件?
    • 您好,您介意在此代码上附加一个开源许可证吗?我想使用它,但由于它目前没有许可,我不能。谢谢。
    • "或与强制性标准 C argv & argc 解析稍有相似:" - 这是不正确的。您正在将“标准 C argv 解析”与“标准 cmd.exe argv 解析”进行比较,根据我的经验,后者远非标准或理智。
    • 您的 win_CommandLineToArgvW 看起来与 list2cmdline 完全相反。
    猜你喜欢
    • 2012-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-28
    • 1970-01-01
    • 1970-01-01
    • 2010-11-27
    相关资源
    最近更新 更多