在 argparse 中使用子命令创建解析器,自定义位置参数

Create parser with subcommands in argparse, customize positional argument(s)

我是这个模块的新手,所以请多多包涵。我有以下代码:

reader.py

import argparse

parent_parser = argparse.ArgumentParser(description="Read text files.")
parent_parser.add_argument('filename', help='TXT file', type=file, nargs='+')
parent_parser.add_argument('--verbose', '-v', action='store_true', 
        help="Verbosity on")

child_parser = parent_parser.add_subparsers(title="subcommand",
        help="Subcommand help")
new_file_command = child_parser.add_parser('new', help="New text file")
edit_file_command = child_parser.add_parser('edit', help="Edit existing text file")

args = parent_parser.parse_args()

我想要实现的可能不是解析器和 unix 命令行实用程序工作方式的标准方式。如果那是真的,请纠正我,因为我想要标准化的应用程序。

这就是我要实现的目标:

同样,我不确定这是否是命令行 apps/scripts 的行为方式。我阅读了文档并查看了示例,但我仍然不清楚子解析器是如何工作的。我试着查看 Click 模块,但在我看来这更复杂。

感谢任何帮助。谢谢!

所以三个示例调用是:

python reader.py some.txt 
python reader.py new another.txt
python reader.py edit some.txt

处理这些问题的最简单方法是使用一个 'optional' 位置,一个需要一个。

parser = ArgumentParser...
parser.add_argument('-v','--verbose', ...)
parser.add_argument('cmd', nargs='?', default='open', choices=['open','edit','new'])
parser.add_argument('filename')

对于你的 3 个样本,它应该产生如下内容:

namespace(cmd='open', filename='some.txt')
namespace(cmd='new', filename='another.txt')
namespace(cmd='edit', filename='some.txt')

cmd 是一个可选的位置参数。如果缺少,则将一个字符串分配给 filenamecmd 得到它的 default。这样做比尝试使 subparsers 可选更容易。


至于您当前的解析器:

parent_parser = argparse.ArgumentParser(description="Read text files.")
parent_parser.add_argument('filename', help='TXT file', type=file, nargs='+')

我不推荐使用 type=file。最好使用 FileType 或默认字符串(它允许您稍后在 with context 中打开文件)。

至于nargs='+',你真的要分配1 or more个字符串给filename吗?还是您在考虑“?”,即 0 or 1,即使其成为可选的?

parent_parser.add_argument('--verbose', '-v', action='store_true', 
        help="Verbosity on")

child_parser = parent_parser.add_subparsers(title="subcommand",
        help="Subcommand help")
new_file_command = child_parser.add_parser('new', help="New text file")
edit_file_command = child_parser.add_parser('edit', help="Edit existing text file")

将接受可变数量值的 filename 位置与 subparsers 参数(期望 newedit 的位置)混合可能是个问题。

我希望 'python reader.py some.txt' 反对缺少 subparser 命令。 'python reader.py new another.txt' 将尝试将 new 分配给 filename,并将 another.txt 分配给子解析器,并引发错误。

最好在所有 3 种情况下都使用 subparsers 命令:

parent_parser = argparse.ArgumentParser(description="Read text files.")
parent_parser.add_argument('--verbose', '-v', action='store_true', 
        help="Verbosity on")
child_parser = parent_parser.add_subparsers(title="subcommand",
        help="Subcommand help", dest='cmd')
open_file_command = child_parser.add_parser('open', help="Open text file")
open_file_command.add_argument('filename', help='TXT file')
new_file_command = child_parser.add_parser('new', help="New text file")
new_file_command.add_argument('filename', help='TXT file')
edit_file_command = child_parser.add_parser('edit', help="Edit existing text file")
edit_file_command.add_argument('filename', help='TXT file')

每个命令 'open'、'new'、'edit' 都需要一个 'filename'.

试图避免使用 open 命令会造成比其价值更多的困难。

(最新的 argparse 中有一个 bug/feature 使子解析器可选,但您不应该在不真正了解问题的情况下利用它。)


有:

parser = argparse.ArgumentParser()
parser.add_argument('-v', '--verbose') 
parser.add_argument('cmd', nargs='?', default='open', 
    choices=['open', 'edit', 'new']) 
parser.add_argument('filename', nargs='+') 

我希望reader.py new customstring给予

namespace(cmd='new', filename=[customstring])

可用作:

if args.cmd=='new':
with open(args.filename[0] + '.txt', 'w') as f:
     # do something with the newly created file

openedit 会使用不同的 open 模式。


请注意,在 Py3 中,subparsers 不是必需的。也就是说,如果未提供其中一个子命令,则不会引发错误。这是对早期版本的无意更改。

Argparse with required subparser