Python 中的命令行参数

概述

由于 Python 是一种非常流行的编程语言,并且支持大多数操作系统和许多使命令行参数处理变得容易的库,因此它被广泛用于创建用于多种目的的命令行工具。这些工具的范围从简单的 CLI 应用程序到更复杂的应用程序,例如 AWS 的应用程序 awscli 工具。

像这样的复杂工具通常由用户通过以下方式控制 命令行参数,它允许用户使用特定命令、设置选项等。例如,这些选项可以告诉工具输出附加信息、从指定源读取数据或将输出发送到某个位置。

一般来说,传递给 CLI 工具的参数会有所不同,具体取决于您的操作系统:

  • 类Unix: - 后面跟着一个字母,比如 -h-- 后面跟着一个词,比如 --help
  • Windows上: / 后跟一个字母或单词,例如 /help

这些不同方法的存在是由于历史原因。类 Unix 系统上的许多程序都支持单破折号和双破折号表示法。单破折号表示法主要与单字母选项一起使用,而双破折号表示更易读的选项列表,这对于需要更明确的复杂选项特别有用。

备注:在本文中,我们将只关注类 Unix 格式 ---.

请记住,参数的名称和含义都是特定于程序的 - 除了一些常见的约定(例如 --help 有关该工具使用的更多信息。作为 Python 脚本的开发人员,您将决定向调用者提供哪些参数以及它们执行的操作。这需要正确的评估。

随着可用参数列表的增长,您的代码在尝试准确解析它们时将变得更加复杂。幸运的是,Python 中有许多库可以帮助您完成此任务。我们将介绍一些最常见的解决方案,其中包括“自己动手”和 sys.argv,采用“为您完成”的方法 argparse.

使用 Python 处理命令行参数

Python 3+ 及其周围的生态系统支持多种不同的处理命令行参数的方法。有 许多 有助于解析命令行参数的库。

内置的方法是使用 sys 模块。就名称及其用法而言,它与 C 库直接相关(libc).

第二种方法是 getopt 模块,它处理短选项和长选项,包括参数值的评估。

argparse模块,这是从 optparse 模块(Python 2.7 之前可用)。

docopt 模块,即 在GitHub上可用,也允许相同的功能。

近日, absl 图书馆也一直在蓬勃发展,作为替代的一种手段 optparsegetopt().

这些方法各有利弊,因此值得对每种方法进行评估,看看哪一种最适合您的需求。

系统模块

这是 Python 早期就附带的一个基本模块。它采用与 C 库非常相似的方法,使用 argc/argv 访问参数。这 系统模块 在名为的简单列表结构中实现命令行参数 sys.argv.

每个列表元素代表一个参数。列表中的第一项, sys.argv[0], 是 Python 脚本的名称。列表的其余元素, sys.argv[1]sys.argv[n],是命令行参数 2 到 n。

使用空格作为参数之间的分隔符。包含空格的参数值必须用引号引起来,以便正确解析 sys.

相当于 argc 只是列表中元素的数量。要获取该值,请使用Python len() 操作员。我们稍后将在代码示例中展示这一点。

打印第一个 CLI 参数

在第一个示例中,我们的脚本将确定它的调用方式。此信息保存在第一个命令行参数中,索引为 0。下面的代码显示了如何获取 Python 脚本的名称:

import sys

print("The script has the name %s" % (sys.argv[0])

将此代码保存在名为的文件中 arguments-program-name.py,然后如下所示调用它。输出如下并包含文件名,包括其完整路径:

$ python arguments-program-name.py
The script has the name arguments-program-name.py
$ python /home/user/arguments-program-name.py
The script has the name /home/user/arguments-program-name.py

从上面的第二次调用可以看到,我们不仅获得了Python文件的名称,还获得了用于调用它的完整路径。

计算参数的数量

在第二个示例中,我们简单地使用内置的命令来计算命令行参数的数量 len() 方法。 sys.argv 是我们必须检查的列表。在下面的代码中,我们获取参数的数量,然后减去 1,因为这些参数之一(即第一个参数)始终设置为文件名,这对我们并不总是有用。因此,用户传递的实际参数数量是 len(sys.argv) - 1:

import sys


arguments = len(sys.argv) - 1
print ("The script is called with %i arguments" % (arguments))

保存该文件并将其命名为arguments-count.py。下面显示了调用此脚本的一些示例。这包括三种不同的场景:

  • 没有任何进一步命令行参数的调用
  • 有两个参数的调用
  • 具有两个参数的调用,其中第二个参数是包含空格的带引号的字符串
$ python arguments-count.py
The script is called with 0 arguments
$ python arguments-count.py --help me
The script is called with 2 arguments
$ python arguments-count.py --option "long string"
The script is called with 2 arguments
迭代参数

我们的第三个示例输出发送到 Python 脚本的每个参数,程序名称本身除外。因此,我们循环遍历以 第二 列表元素。回想一下,这是索引 1,因为 Python 中的列表是从 0 开始的:

import sys


arguments = len(sys.argv) - 1


position = 1
while (arguments >= position):
    print ("Parameter %i: %s" % (position, sys.argv[position]))
    position = position + 1

下面我们调用我们的代码,它被保存到文件arguments-output.py中。与我们前面的示例一样,输出说明了三种不同的调用:

  • 没有任何参数的调用
  • 有两个参数的调用
  • 具有两个参数的调用,其中第二个参数是包含空格的带引号的字符串
$ python arguments-output.py
$ python arguments-output.py --help me
Parameter 1: --help
Parameter 2: me
$ python arguments-output.py --option "long string"
Parameter 1: --option
Parameter 2: long string

请记住,显示带引号的字符串示例的要点是参数通常由空格分隔, 除非 它们被引号包围。

绳索下降旗帜(absl)

Abseil 的 Flags 库旨在通过分布式命令行参数将命令行参数引入生产环境。当一个模块使用命令行标志并导入到另一个模块时——另一个模块 也导入标志,并可以通过将它们转发到导入的模块来处理它们。

这使得模块之间共享的复杂命令行参数变得更容易且更简洁。

此外,该库允许您定义参数的默认值、描述和数据类型,因此不需要额外的检查和转换。

from absl import flags
import sys


flags.DEFINE_string('name', 'User', 'The name of the user.')


FLAGS = flags.FLAGS
FLAGS(sys.argv)

print(f"Hello {FLAGS.name}!")

支持的数据类型是:

  • DEFINE_integer()
  • DEFINE_string()
  • DEFINE_bool()
  • DEFINE_enum()
  • DEFINE_list()
  • DEFINE_float()

以及 DEFINE_multi_integer(), DEFINE_multi_string()DEFINE_multi_enum() 用于多参数输入。此外,运行 --help, --helpfull等以不同的格式打印现有的标志及其描述。

查看我们的 Git 学习实践指南,其中包含最佳实践、行业认可的标准以及随附的备忘单。 停止谷歌搜索 Git 命令,实际上 学习 它!

该库还允许您定义验证 - 无论是在范围方面,例如基于整数的值具有 upper_bound or lower_bound 这是可以接受的,并运行任意方法来检查值:

def validate_name(value):
    return len(value) > 15

flags.register_validator('name',
                         validate_name,
                         message='Name is over 15 characters long.',
                         flag_values=FLAGS)

将这些收集到一个具体的例子中:

from absl import flags
import sys

flags.DEFINE_string('name', 'User', 'The name of the user.')
flags.DEFINE_integer('tasks', 0, 'The number of tasks a user has.', lower_bound=0)

FLAGS = flags.FLAGS
FLAGS(sys.argv)

print(f"{FLAGS.name} has {FLAGS.tasks} tasks to work on.")
$ python flags.py --name=John --tasks=5
John has 5 tasks to work on.
$ python flags.py --name=John --tasks=-1

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/absl/flags/_flag.py", line 180, in _parse
    return self.parser.parse(argument)
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/absl/flags/_argument_parser.py", line 168, in parse
    raise ValueError('%s is not %s' % (val, self.syntactic_help))
ValueError: -1 is not a non-negative integer
...

argparse 模块

argparse模块 自 Python 3.2 起可用,并且增强了 optparse Python 2.7 之前存在的模块。 Python 文档包含 API 描述和详细介绍所有方法的教程。

该模块提供具有标准化输出的命令行界面,而前两种解决方案将大部分工作交给您。 argparse 允许验证固定和可选参数,名称检查为短或长样式。作为默认可选参数,它包括 -h,以及它的长版本 --help。该参数附有描述接受的参数的默认帮助消息。

下面的代码显示了解析器初始化,下面的输出显示了基本调用,后面是帮助消息。与我们在前面的示例中使用的 Python 调用相反,请记住在这些示例中使用 Python 3:


import argparse


parser = argparse.ArgumentParser()
parser.parse_args()
$ python3 arguments-argparse-basic.py 
$ python3 arguments-argparse-basic.py -h
usage: arguments-argparse-basic.py [-h]

optional arguments:
  -h, --help  show this help message and exit
$ python3 arguments-argparse-basic.py --verbose
usage: arguments-argparse-basic.py [-h]
arguments-argparse-basic.py: error: unrecognized arguments: --verbose

在下一步中,我们将为用户添加自定义描述到帮助消息中。以这种方式初始化解析器允许附加文本。下面的代码将描述存储在 text 变量,它被显式地赋予 argparse 类作为 description 范围。调用下面的代码,您可以看到输出是什么样的:


import argparse


text = 'This is a test program. It demonstrates how to use the argparse module with a program description.'


parser = argparse.ArgumentParser(description=text)
parser.parse_args()
$ python3 arguments-argparse-description.py --help
usage: arguments-argparse-description.py [-h]

This is a test program. It demonstrates how to use the argparse module with a
program description.

optional arguments:
  -h, --help  show this help message and exit

作为最后一步,我们将添加一个名为的可选参数 -V,它有一个名为的相应长样式参数 --version。为此,我们使用以下方法 add_argument() 我们用三个参数调用(显示为 --version, 仅有的):

  • 参数名称: --version
  • 参数的帮助文本: help="show program version"
  • 行动(无附加价值): action="store_true"

其源代码如下所示。将参数读入名为的变量中 args 是通过 parse_args() 的方法 parser 目的。请注意,您在一次调用中同时提交了短版本和长版本。最后,检查属性是否 args.V or args.version 设置并输出版本消息:


import argparse


parser = argparse.ArgumentParser()
parser.add_argument("-V", "--version", help="show program version", action="store_true")


args = parser.parse_args()


if args.version:
    print("This is myprogram version 0.1")
$ python3 arguments-argparse-optional.py -V
This is myprogram version 0.1
$ python3 arguments-argparse-optional.py --version
This is myprogram version 0.1

--version argument 不需要在命令行上给出值。这就是为什么我们将操作参数设置为 "store_true"。在其他情况下,您可能需要额外的指定值,例如,如果您指定特定的体积、高度或宽度。这将在下一个示例中显示。作为默认情况,请注意所有参数都被解释为字符串:


import argparse


parser = argparse.ArgumentParser()


parser.add_argument("--width", "-w", help="set output width")


args = parser.parse_args()


if args.width:
    print("Set output width to %s" % args.width)

在这里,我们展示了提交不同参数值时会发生什么。这包括短版本和长版本,以及帮助消息:

$ python3 arguments-argparse-optional2.py -w 10
Set output width to 10
$ python3 arguments-argparse-optional2.py --width 10
Set output width to 10
$ python3 arguments-argparse-optional2.py -h
usage: arguments-argparse-optional2.py [-h] [--width WIDTH]

optional arguments:
  -h, --help            show this help message and exit
  --width WIDTH, -w WIDTH
                        set output width

getopt 模块

正如您之前可能已经注意到的, sys 模块仅将命令行字符串拆分为单个方面。蟒蛇 获取选择模块 更进一步,通过参数验证扩展输入字符串的分离。基于 getopt C 函数,它允许短选项和长选项,包括赋值。

在实践中,它需要 sys 模块正确处理输入数据。为此,双方 sys 模块和 getopt 必须预先加载模块。接下来,从输入参数列表中删除第一个列表元素(请参见下面的代码),并将剩余的命令行参数列表存储在名为的变量中 argument_list:


import getopt, sys


full_cmd_arguments = sys.argv


argument_list = full_cmd_arguments[1:]

print argument_list

中的论点 argument_list 现在可以使用解析 getopts() 方法。但在此之前,我们需要告诉 getopts() 关于哪些参数有效。它们的定义如下:

short_options = "ho:v"
long_options = ["help", "output=", "verbose"]

这意味着我们认为这些参数是有效的,以及一些额外的信息:

------------------------------------------
long argument   short argument  with value
------------------------------------------
--help           -h              no
--output         -o              yes
--verbose        -v              no
------------------------------------------

你可能已经注意到 o 短选项由冒号进行, :。 这说明 getopt 该选项应该被分配一个值。

现在,我们可以处理参数列表。这 getopt() 方法需要配置三个参数 - 实际参数列表 argv,以及有效的短选项和长选项(如前面的代码片段所示)。

方法调用本身保存在 try-catch 语句中,以覆盖评估期间的错误。如果发现参数不属于之前定义的列表的一部分,则会引发异常。 Python 脚本会将错误消息打印到屏幕上,并以错误代码 2 退出:

try:
    arguments, values = getopt.getopt(argument_list, short_options, long_options)
except getopt.error as err:
    
    print (str(err))
    sys.exit(2)

最后,参数及其对应的值被存储在名为的两个变量中 argumentsvalues。现在,您可以轻松地在代码中评估这些变量。我们可以使用一个 for-loop 循环访问已识别的参数列表,一个条目一个接着一个条目。


for current_argument, current_value in arguments:
    if current_argument in ("-v", "--verbose"):
        print ("Enabling verbose mode")
    elif current_argument in ("-h", "--help"):
        print ("Displaying help")
    elif current_argument in ("-o", "--output"):
        print (("Enabling special output mode (%s)") % (current_value))

您可以在下面看到执行此代码的输出。我们将展示程序如何对有效和无效的程序参数做出反应:

$ python arguments-getopt.py -h
Displaying help
$ python arguments-getopt.py --help
Displaying help
$ python arguments-getopt.py --output=green --help -v
Enabling special output mode (green)
Displaying help
Enabling verbose mode
$ python arguments-getopt.py -verbose
option -e not recognized

对我们程序的最后一次调用一开始可能看起来有点令人困惑。要理解它,您需要知道速记选项(有时也称为标志)可以与单个破折号一起使用。这使您的工具可以更轻松地接受许多选项。例如,调用 python arguments-getopt.py -vh 与调用相同 python arguments-getopt.py -v -h。所以在上面的最后一个调用中, getopt 模块认为用户试图通过 -e 作为一个选项,这是无效的。

结论

在本文中,我们展示了在 Python 中检索命令行参数的许多不同方法,包括使用 sys, getoptargparse。这些模块的功能各不相同,有些模块提供的功能比其他模块多得多。 sys 是完全灵活的,而两者 getoptargparse 需要一些结构。相比之下,它们涵盖了大部分复杂的工作 sys 由你决定。在完成所提供的示例之后,您应该能够确定哪个模块最适合您的项目。

在本文中,我们没有讨论其他解决方案,例如 docopts 模块,我们刚刚提到过。该模块采用完全不同的方法,将在下一篇文章中详细解释。

参考资料

时间戳记:

更多来自 堆栈滥用