python , Windows : parsing command lines with shlex

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

当您必须拆分命令行时,例如调用 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=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:

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/

相关文章:

python - 未绑定(bind)本地错误 : local variable 'form' referenced before assignment in Django

java - 有没有办法从 windows 或 linux 机器远程安装程序到 windows 机器?最好是一些容易用java编码的东西

windows - npm windows 全局安装导致 npm ERR!外来的

windows - 如何让 Internet Explorer 正确处理自定义协议(protocol)处理程序?

c - 尚未创建的文件

R 命令行将文件名传递给参数中的脚本 (Windows)

command-line - 是否可以在上一个/下一个终端命令提示符之间跳转?

python - Pandas 样式的默认 float 格式

python - 如何关闭一个图形或替换一个图形而不必手动关闭 Python/pylab 中的每个图形?

python - 使用 MySQLdb 从 Python 中的 MySQL 存储过程获取返回值