【问题标题】:How to split a string into command line arguments like the shell in python?如何将字符串拆分为命令行参数,如 python 中的 shell?
【发布时间】:2017-12-10 06:41:04
【问题描述】:

我在一个字符串中有命令行参数,我需要将其拆分以提供给argparse.ArgumentParser.parse_args

我看到the documentation 大量使用string.split()。但是在复杂的情况下,这是行不通的,比如

--foo "spaces in brakets"  --bar escaped\ spaces

在 python 中有这样的功能吗?

here 提出了类似的 java 问题)。

【问题讨论】:

  • argparse.ArgumentParser.parse_args 的确切输出应该是什么
  • 您需要向我们展示一个完整的程序来演示您遇到的具体问题,并提供触发它的示例输入。

标签: python argparse


【解决方案1】:

您可以使用 click 包中的 split_arg_string 辅助函数:

import re

def split_arg_string(string):
    """Given an argument string this attempts to split it into small parts."""
    rv = []
    for match in re.finditer(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
                             r'|"([^"\\]*(?:\\.[^"\\]*)*)"'
                             r'|\S+)\s*', string, re.S):
        arg = match.group().strip()
        if arg[:1] == arg[-1:] and arg[:1] in '"\'':
            arg = arg[1:-1].encode('ascii', 'backslashreplace') \
                .decode('unicode-escape')
        try:
            arg = type(string)(arg)
        except UnicodeError:
            pass
        rv.append(arg)
    return rv

例如:

>>> print split_arg_string('"this is a test" 1 2 "1 \\" 2"')
['this is a test', '1', '2', '1 " 2']

click 包开始主导命令参数解析,但我认为它不支持从字符串解析参数(仅来自 argv)。上面的辅助函数仅用于bash 补全。

编辑:我只能按照@ShadowRanger 的回答建议使用shlex.split()。我不删除这个答案的唯一原因是因为它提供了更快的拆分,然后是shlex 中使用的成熟的纯python 标记器(对于上面的示例,大约快 3.5 倍, 5.9us 与 20.5us)。但是,这不应该是比shlex 更喜欢它的理由。

【讨论】:

    【解决方案2】:

    【讨论】:

    • 不错!它从 Python 2.3 开始可用。
    • shlex.split 是否有转义引号的问题?例如--foo "bar\"baz"
    • @user1735003: 是的,尽管它通常会由 shell 为您处理(shlex 遵循与sh shell 规则大致相同的规则)。但是如果你有一个这样的命令行,它就可以了,这就是shlex 的全部意义:shlex.split(r'--foo "bar\"baz"') 产生['--foo', 'bar"baz']。当argparse 文档使用str.split 而不是shlex.split(或显式列表)时,它们是being lazy;他们是为了简洁,但没有需要shlex 知识的精神负担。
    • @Eric:鉴于没有单一的 Windows 格式(Windows 可执行文件接收原始字符串并自己解析它),并且问题是关于解析一个字符串以供 argparse 使用(它有一个固定的行为,无论操作系统如何),您的评论似乎与这种情况并不特别相关。
    • 尽管 windows 接收到原始字符串,但大多数程序定义了一个 main(argc, argv),最终使用其 C 运行时提供的解析。 argparse 具有与操作系统无关的固定行为,但这是因为它将字符串列表作为输入,通常是 sys.argvsys.argv 的填充方式 依赖于平台,值得引起注意。 shlex.split 匹配 sys.argv 在 posix 系统上的填充方式,但不匹配它在 Windows 系统上的填充方式。
    【解决方案3】:

    如果您正在解析 Windows 样式的命令行,则 shlex.split 无法正常工作 - 对结果调用 subprocess 函数与将字符串直接传递给 shell 的行为不同。

    在这种情况下,将字符串(如命令行参数)拆分为 python 的最可靠方法是...将命令行参数传递给 python:

    import sys
    import subprocess
    import shlex
    import json  # json is an easy way to send arbitrary ascii-safe lists of strings out of python
    
    def shell_split(cmd):
        """
        Like `shlex.split`, but uses the Windows splitting syntax when run on Windows.
    
        On windows, this is the inverse of subprocess.list2cmdline
        """
        if os.name == 'posix':
            return shlex.split(cmd)
        else:
            # TODO: write a version of this that doesn't invoke a subprocess
            if not cmd:
                return []
            full_cmd = '{} {}'.format(
                subprocess.list2cmdline([
                    sys.executable, '-c',
                    'import sys, json; print(json.dumps(sys.argv[1:]))'
                ]), cmd
            )
            ret = subprocess.check_output(full_cmd).decode()
            return json.loads(ret)
    

    这些差异的一个例子:

    # windows does not treat all backslashes as escapes
    >>> shell_split(r'C:\Users\me\some_file.txt "file with spaces"', 'file with spaces')
    ['C:\\Users\\me\\some_file.txt', 'file with spaces']
    
    # posix does
    >>> shlex.split(r'C:\Users\me\some_file.txt "file with spaces"')
    ['C:Usersmesome_file.txt', 'file with spaces']
    
    # non-posix does not mean Windows - this produces extra quotes
    >>> shlex.split(r'C:\Users\me\some_file.txt "file with spaces"', posix=False)
    ['C:\\Users\\me\\some_file.txt', '"file with spaces"']  
    

    【讨论】:

      猜你喜欢
      • 2015-05-23
      • 2015-12-17
      • 1970-01-01
      • 1970-01-01
      • 2011-03-16
      • 2014-09-18
      • 1970-01-01
      • 2020-08-19
      • 2018-05-01
      相关资源
      最近更新 更多