argparse 可以用于处理复杂的混合匹配命令行参数(无选项)吗?

Can argparse be used to handle complex mix-and-match command line arguments (no options)?

我正在将 shell 脚本转换为 Python。

shell 脚本支持这样的参数。

Usage: foo [-h] [-v] [COMMAND] [COMMAND_ARG]...

One or more of the following commands may be specified in the same
command line invocation of this script.

Commands:
  print            Print lines in CSV.
  search PATTERN   Print only lines that match PATTERN.
  remove PATTERN   Remove all lines that match PATTERN.
  convert          Convert CSV file to XLS file.
  limit N          Show a maximum of N results (default 10).
  all              Do not limit lines.

Options:
  -h, --help       Show help.
  -v, --version    Show version.

'Commands' 部分中列出的一个或多个参数可以混合使用 匹配,但其中一些是互斥的。

例如,以下所有都是有效的命令行调用。

# Prints 10 lines from CSV
foo print

# Prints 3 lines from CSV
foo print limit 3
foo limit 3 print

# Prints all lines from CSV
foo print all
foo all print

# Converts 10 lines from CSV into XLS
foo convert

# Converts 3 lines from CSV into XLS
foo limit 3 convert
foo convert limit 3

# Search for the word "foo" in CSV and print 3 such lines
foo search "\<foo\>" limit 3
foo limit 3 search "\<foo\>"

但是,printsearchremoveconvert是相互的 独家的。所以以下是无效的。

foo print convert
foo search "\<foo\>" remove "\<foo\>"

是否可以使用以下方式处理命令行参数 argparse?还是我自己循环 sys.argv 更好, 用我自己的代码处理这些情况,这也意味着我有 负责更新帮助消息和任何必要的验证 每当支持的参数列表发生变化时?

注意:我不想依赖 argparse 模块的任何内部实现来实现此目的。我只想依赖公开记录的 API of argparse。如果用 argparse 的公开记录的 API 无法解决这个问题,那也没关系,这就是答案。在那种情况下,我会通过循环 sys.argv 自己处理命令行参数,类似于 shell 脚本通过循环 "$@".

处理参数的方式。

它基本上可以处理这个问题,只是与您的处理方式不太一样。您将使用 subcommands/subparsers 进行打印、搜索、删除和转换,并将其他 "commands"(实际上是顶级命令上的选项)定义为开关,如果它们很常见,则可以在顶级解析器上使用对所有命令,或对个人 subparsers/subcommands 如果它们不常见。

现在,在这种特殊情况下,您似乎甚至没有为每个子命令设置独特的行为,因此您可能只需要一个位置参数和两个开关即可:

parser = argparse.ArgumentParser()
parser.add_argument('action', choices=('print', 'search', 'remove', 'convert')
parsecount = parser.add_mutually_exclusive_group()
parsecount.add_argument('--all', dest='limit', action='store_const', const=None)
parsecount.add_argument('--limit', type=int)
parser.set_defaults(limit=10)

如果需要,位置 action 参数可以更改为单独的子解析器,但在这种情况下,似乎所有可能的命令都允许 alllimit

一种变体,其中 --limit--all 是可选的,其余的是子解析器,非常适合 argparse

您不能 运行 多个子解析器(例如 printlimit)而不进行一些严重的卷积。另外 limitall 在概念上是非常不同的论点。其他的是命令——如此做。 limitall 是修饰符。

limitall 与所有其他人一起工作,或者只与 printsearch 一起工作。它们作为那些子解析器的可选项可能更有意义。

另外请记住,argparse 的一大优点是它会生成用法、帮助和错误消息。当您做一些不寻常的事情时,您需要考虑这些消息。您如何清楚地告诉您的用户他们可以将 limitprint 一起使用,但不能与 convert 一起使用?或者他们不能同时使用 limitall;或者一个覆盖另一个。

但是,如果您受困于这些 names/flags,并且无法添加 --,那么请不要理会 argparse。不要费心去改变已经有效的东西。

argparse(以及 optparsegetopt)中的基本理念是在您希望将内容与操作匹配的地方使用带标记的字符串 ('--'),并处理其余按顺序。 argparse 期望根据位置而不是内容来解析您的字符串。