可以用Python的argparse重构命令行吗?

6 投票
2 回答
994 浏览
提问于 2025-04-17 15:34

我有一个Python脚本,它可以读取一个文件,这个文件里包含了某个工具的命令行调用。我想在调用这个工具之前,修改一下这些调用的选项。比如,我可能会把:

my_util --input file1.txt --option1 red --option2 blue

...改成这样:

my_util --input file1_001.txt --option1 red --option3 green

(更准确地说,我会把参数当成列表来处理。)

我觉得使用argparse模块是最简单的方法:我可以解析这些参数,根据需要修改、添加或删除选项,然后重新构建命令行。

但是,最后一步我该怎么做呢?给定Namespace对象,它是通过parse_args()返回的,我能否轻松地重新构建一个命令行选项的列表,这样就可以传递给subprocess.Popen()

2 个回答

0

我知道这个问题已经有点老了,但我最近也遇到了同样的问题。我发现我只需要一种方法来遍历这些Action对象。可惜的是,ArgParser本身并没有直接提供这个内部列表。不过,这些对象是通过add_argument()返回的,所以我可以自己构建一个列表。老实说,在每次调用时都加上actions.append()让我觉得太麻烦了,所以我把所有选项存储在一个元组里:

def add_argument(*args, **kwargs):
    return (args, kwargs)

parser = argparse.ArgumentParser()
options = (
    add_argument('--verbose', action='store_true'),
    add_argument('--author'),
    add_argument('--subject', required=True),
    add_argument('--cache', nargs='?'),
    add_argument('files', nargs=''),
)
actions = []
for (args, kwargs) in options:
    actions.append(parser.add_argument(*args, **kwargs))
args = parser.parse_args()

到这个时候,选项已经在args中解析好了,所有的argparse.Action对象都存储在actions列表里。接下来,我可以遍历这个列表,像这样重建选项:

cmdline = []
for action in actions:
    value = getattr(args, action.dest)
    if action.required or value != action.default:
        if action.option_strings:
            cmdline.append(action.option_strings[0])
        if action.nargs is None:
            cmdline.append(value)
        elif action.nargs == '?':
            if value != action.const:
                cmdline.append(value)
        elif action.nargs != 0:
            cmdline += value

在我的具体情况下,我还想从命令行中删除一些选项。为此,我只是通过单独调用parser.add_argument()来添加它们,而不是通过options元组。

3

命名空间对象其实就是一个简单的对象子类,所以你可以用 vars 来把里面的值提取出来,变成一个字典:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo')
>>> args = parser.parse_args(['--foo', 'BAR'])
>>> vars(args)
{'foo': 'BAR'}

或者你也可以直接给一个类赋值,这样就能把参数提取成类变量:

>>> class C(object):
...     pass
...
>>> c = C()
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo')
>>> parser.parse_args(args=['--foo', 'BAR'], namespace=c)
>>> c.foo
'BAR'

用这两种结构来测试或替换参数,然后把结果传给 Popen 是相对简单的。

撰写回答