Python命令行工具详解

Python命令行工具

1. 引言

1.1 背景

在Python编程领域,命令行工具作为一种重要的用户交互方式,为程序提供了灵活性和便捷性。随着Python应用的多样化,命令行工具的需求也日益增加。

1.2 目的

旨在对Python命令行工具进行深入的调研和分析,以便开发者能够根据实际需求选择合适的工具,提高开发效率和用户体验。

2. Python命令行工具类型

在Python中,有多个用于创建命令行界面的库。optparseargparseclickfire是其中的四个。

2.1. optparse (已弃用)

概述

  • optparse 是Python 2中的一个旧库,用于解析命令行选项。
  • 在Python 2.7之后的版本中,官方推荐使用argparse替代optparse

特点

  • 相对简单,易于上手。
  • 不支持某些现代CLI特性,如子命令。
  • 已不再维护,不推荐新项目使用。

2.2. argparse

概述

  • argparse 是Python标准库的一部分,自Python 2.7起可用,是optparse的替代品。
  • 用于编写用户友好的命令行接口。
  • 支持子命令、参数、选项以及帮助信息的自动生成。

特点

  • 功能强大,能够满足大多数CLI需求。
  • 自动处理 -h/--help 选项。
  • 支持参数类型检查和默认值设置。
  • 通过继承argparse.ArgumentParser可以扩展功能。

2.3. click

概述

  • click 是一个第三方库,旨在通过声明式的方式创建复杂的命令行界面。
  • 由Armin Ronacher开发,是Flask项目的一部分。
  • 提供了装饰器和上下文管理来简化CLI的开发。

特点

  • 强调可读性和简洁性。
  • 支持命令、选项、参数和子命令的声明式定义。
  • 提供了丰富的内置类型和验证器。
  • 良好的文档和社区支持。

2.4. fire

概述

  • fire 是一个轻量级的Python CLI库,由Google开发。
  • 旨在简化从Python函数创建命令行界面的过程。
  • 提供了简单的API来注册函数作为命令行操作。

特点

  • 极简主义设计,易于集成到现有代码中。
  • 自动生成帮助信息。
  • 支持参数和子命令。
  • 适合快速原型开发和小项目。

3. 常用Python命令行工具库介绍

3.1 optparse(弃用)

API函数描述示例
add_option添加一个新的命令行选项parser.add_option(“-f”, “–file”, dest=“filename”, help=“…”)
add_options添加多个命令行选项parser.add_options(opt1, opt2, …)
parse_args解析命令行参数(options, args) = parser.parse_args()
get_option获取特定选项的值value = options.get_option(“–file”)
print_help打印帮助信息parser.print_help()
error输出错误信息并退出parser.error(“Invalid input”)
示例
from optparse import OptionParser

parser = OptionParser()
parser.add_option("-f", "--file", dest="filename", help="read data from FILE", metavar="FILE")
parser.add_option("-q", "--quiet", action="store_false", dest="verbose", default=True, help="don't print status messages to stdout")

(options, args) = parser.parse_args()

if options.verbose:
    print("verbosity is on")
if options.filename:
    print(f"read data from file: {options.filename}")

3.2 argparse

argparse 常用API
API函数描述示例
add_argument添加一个新的命令行参数parser.add_argument(‘integers’, metavar=‘N’, type=int, nargs=‘+’, help=‘…’)
add_mutually_exclusive_group添加互斥参数组group = parser.add_mutually_exclusive_group(required=True)
add_subparsers添加子解析器subparsers = parser.add_subparsers(dest=‘command’)
parse_known_args解析已知参数args, unknown = parser.parse_known_args()
set_defaults设置默认参数值parser.set_defaults(verbose=True)
print_usage打印用法信息parser.print_usage()
print_help打印帮助信息parser.print_help()
exit退出程序parser.exit(status=0, message=“…”)
add_argument 参数
参数名称描述
name or flags一个命名或者一个选项字符串的列表,例如 foo 或 -f, --foo。
action当参数在命令行中出现时使用的动作基本类型。
nargs命令行参数应当消耗的数目。
const被一些 action 和 nargs 选择所需求的常数。
default当参数未在命令行中出现并且也不存在于命名空间对象时所产生的值。
type命令行参数应当被转换成的类型。
choices由允许作为参数的值组成的序列。
required此命令行选项是否可省略 (仅选项可用)。
help一个此选项作用的简单描述。
metavar在使用方法消息中使用的参数值示例。
dest被添加到 parse_args() 所返回对象上的属性名。
示例
import argparse

# 创建 ArgumentParser 对象
parser = argparse.ArgumentParser(description="这是一个命令行参数示例")

# 添加命令行参数
parser.add_argument("-n", "--name", action="store", type=str, required=True, help="输入你的名字", dest="username")
parser.add_argument("-a", "--age", action="store", type=int, default=18, help="输入你的年龄", dest="userage")
parser.add_argument("-g", "--gender", action="store", choices=["male", "female", "other"], type=str, default="other", help="输入你的性别", dest="usergender")

# 解析命令行参数
args = parser.parse_args()

# 输出解析后的参数值
print(f"用户名: {args.username}")
print(f"年龄: {args.userage}")
print(f"性别: {args.usergender}")

在这个示例中,我们定义了三个命令行参数:-n--name-a--age-g--gender。以下是每个参数的详细说明:

参数名称描述值/示例
name or flags-n 或 --name张三
actionstore(默认)
typestr(默认)
requiredTrue
help输入你的名字
destusername
参数名称描述值/示例
name or flags-a 或 --age25
actionstore(默认)
typeint(默认)
default18
help输入你的年龄
destuserage
参数名称描述值/示例
name or flags-g 或 --gendermale
actionstore(默认)
typestr(默认)
choices[“male”, “female”, “other”]
defaultother
help输入你的性别
destusergender

3.3 click

Click 实际上是通过对 optparse 的一个分支进行封装而实现的,它本身并不进行解析。不基于 argparse 的原因是 argparse 从设计上不允许多个命令的嵌套, 同时在兼容处理 POSIX 参数时有一些不足。

click 常用API
API函数描述示例
command定义一个新的命令@click.command()
option定义一个新的命令行选项@click.option(‘–count’, default=1, help=‘…’)
argument定义一个新的命令行参数@click.argument(‘name’)
pass_context传递上下文给命令函数@click.command(pass_context=True)
CommandCollection将命令集合起来,形成完整的命令行接口if name == ‘main’: click.CommandCollection(info)
Group创建一个命令组group = click.Group(‘group_name’)
MultiCommand创建一个多命令对象multi = click.MultiCommand(‘multi’, ‘Handle multiple commands.’)
Click Options

Click 库提供了丰富的命令行选项定义方式,可以帮助开发者轻松地创建功能强大且易于使用的命令行应用程序。

选项命名

  • 选项名称可以用短横线(-)或双短横线(–)前缀,也可以直接使用参数名。
  • Click 会根据选项的定义顺序选择合适的选项名称,并将其转换为 Python 参数名。

基本选项类型

  • 数值选项: 接受一个参数,可以指定默认值和数据类型,默认类型为字符串。
  • 必需选项: 使用 required=True 将选项设置为必需的,用户必须在命令行中提供值。
  • 显示默认值: 使用 show_default=True 在帮助信息中显示选项的默认值。

多值选项

  • 固定数量参数: 使用 nargs=n 定义接受固定数量参数的选项,参数值将以元组形式存储。
  • 可变数量参数: 使用 multiple=True 定义可以接受多次相同选项的选项,参数值将以列表形式存储。
  • 计数: 使用 count=True 定义可以重复使用的选项,重复次数将被记录为整数。

布尔选项

  • 使用 / 分隔两个选项,分别表示启用和禁用选项,例如 --shout/--no-shout
  • 可以使用 is_flag=True 手动指定选项为布尔选项。

特性选项

  • 可以使用 flag_value 参数定义特性选项,例如 --upper/--lower

选择选项

  • 使用 click.Choice 类型定义可以从多个选项中选择的值,例如 --hash-type=MD5

提示输入

  • 使用 prompt 参数定义提示用户输入的选项,如果用户未在命令行中提供值,Click 将提示输入。
  • 使用 hide_input=True 定义隐藏输入的选项,例如密码输入。
  • 使用 password_option() 装饰器简化密码输入选项的定义。

动态默认值

  • 可以使用可调用对象作为默认值,以便根据环境或配置动态获取默认值。

回调函数和优先处理选项

  • 使用 is_eager=True 定义优先处理的选项,这些选项将在其他选项之前处理。
  • 使用 callback 参数定义回调函数,用于在选项处理完成后执行特定操作。

确认选项

  • 使用 confirmation_option() 装饰器或 --yes 选项,要求用户确认操作。

环境变量

  • 使用 auto_envvar_prefix 参数定义环境变量前缀,自动创建对应环境变量。
  • 使用 envvar 参数指定环境变量名,手动获取环境变量的值。

其他前缀字符

  • 可以使用 prefix_chars 参数定义其他前缀字符,例如 /

范围选项

  • 使用 IntRangeFloatRange 类型定义值必须在特定范围内。

回调函数验证

  • 使用 callback 参数定义验证函数,对选项值进行自定义验证。

可选值

  • 使用 is_flag=Falseflag_value 定义可选值的选项,如果仅使用选项名,则使用 flag_value 作为值。
  • 使用 prompt_required=False 限制提示输入的条件。
综合示例
import click
from click import Choice, IntRange, password_option

# 动态默认值
def get_default_name():
    return "World"

# 回调函数验证
def validate_name(ctx, param, value):
    if not value.isalpha():
        raise click.BadParameter("Name must contain only letters.")
    return value.upper()

# 确认选项装饰器
def confirmation_option(*param_decls, **attrs):
    def decorator(f):
        attrs.setdefault('prompt', 'Are you sure?')
        attrs.setdefault('is_flag', True)
        attrs.setdefault('default', False)
        attrs.setdefault('show_default', True)
        attrs.setdefault('help', 'Confirm the action.')
        click.option(*param_decls, **attrs)(f)
        return f
    return decorator

@click.command()
@click.argument('name', required=True, callback=validate_name)
@click.option('-a', '--age', type=int, default=18, show_default=True, help="Age of the person.")
@click.option('--upper/--lower', default=None, help="Print uppercase or lowercase.")
@click.option('--shout/--no-shout', default=False, help="Shout the message.")
@click.option('--hash-type', type=Choice(['MD5', 'SHA1', 'SHA256']), help="Choose a hash algorithm.")
@click.option('--password', hide_input=True, help="Enter a password.")
@click.option('--password2', is_flag=True, flag_value='password', prompt_required=False, help="Use this as the password.")
@password_option(help="Enter a secure password.")
@click.option('--repeat', count=True, help="Repeat the message.")
@click.option('--number', nargs=2, type=int, help="Specify two numbers.")
@click.option('--numbers', multiple=True, type=int, help="Specify multiple numbers.")
@click.option('--range', type=IntRange(1, 100), help="Specify a number between 1 and 100.")
@click.option('--confirm', is_flag=True, callback=lambda ctx, param, value: value if value else ctx.abort(), help="Confirm the action.")
@confirmation_option('--yes', help="Confirm without prompting.")
@click.option('--default-name', default=get_default_name, help="Default name.")
@click.option('--prompt-name', prompt=True, help="Prompt for a name.")
@click.option('--env-name', envvar='USER_NAME', help="Name from environment variable.")
@click.option('--prefix-name', '/name', help="Name with custom prefix.")
def greet(name, age, upper, shout, hash_type, password, password2, password3, repeat, number, numbers, _range, confirm, yes, default_name, prompt_name, env_name, prefix_name):
    # 处理参数
    if shout:
        name = name.upper()
    elif upper:
        name = name.upper()
    elif upper is False:
        name = name.lower()

    # 处理哈希类型
    if hash_type:
        click.echo(f"Selected hash type: {hash_type}")

    # 处理密码
    if password:
        click.echo("Password provided.")

    if password2:
        click.echo(f"Password set to: {password2}")

    if password3:
        click.echo("Secure password provided.")

    # 重复输出
    for i in range(repeat):
        click.echo(f"Hello, {name}! You are {age} years old.")

    # 输出数字
    if number:
        click.echo(f"Numbers: {number}")
    if numbers:
        click.echo(f"Numbers list: {numbers}")

    # 输出范围内的数字
    if _range:
        click.echo(f"Range: {_range}")

    # 确认动作
    if confirm or yes:
        click.echo("Action confirmed.")
    else:
        click.echo("Action not confirmed.")

    # 显示默认值
    click.echo(f"Default name: {default_name}")

    # 显示提示输入的值
    click.echo(f"Prompt name: {prompt_name}")

    # 显示环境变量的值
    click.echo(f"Environment name: {env_name}")

    # 显示带有自定义前缀的值
    click.echo(f"Prefix name: {prefix_name}")

if __name__ == '__main__':
    greet()
示例解释
  1. 必需参数 (name):

    • 使用 validate_name 回调函数验证名字只包含字母,并将其转换为大写。
  2. 基本选项 (age):

    • 具有默认值和帮助信息。
  3. 布尔选项 (upper, shout):

    • 控制输出的大写或小写。
  4. 选择选项 (hash-type):

    • 选择加密算法。
  5. 提示输入 (prompt-name):

    • 如果用户没有提供值,程序会提示输入。
  6. 环境变量 (env-name):

    • 从环境变量 USER_NAME 获取值。
  7. 多值选项 (numbers):

    • 用户可以多次使用相同的选项来提供多个值。
  8. 计数 (repeat):

    • 重复输出消息。
  9. 范围选项 (_range):

    • 用户必须提供一个介于 1 到 100 之间的整数。
  10. 确认选项 (confirm, --yes):

    • 用户必须确认动作才能继续。
  11. 动态默认值 (default-name):

    • 根据函数 get_default_name 返回的值设置默认值。
  12. 其他前缀字符 (prefix-name):

    • 使用 / 作为前缀字符。
  13. 密码输入 (password, password2, password3):

    • 使用 hide_input=True 隐藏输入,以及 password_option 装饰器简化密码输入。
  14. 回调函数验证 (validate_name):

    • 验证名字只包含字母。

3.4 fire

fire 常见API
设置命令备注
安装pip install fire从PyPI安装Fire
创建命令行界面命令备注
导入import fire
调用fire.Fire()将当前模块转换为Fire命令行界面(CLI)。
调用fire.Fire(component)component转换为Fire命令行界面(CLI)。
使用命令行界面命令备注
帮助command --help显示帮助页面。
交互式模式command -- --interactive进入交互模式。
分隔符command -- --separator=X此设置将分隔符设为X。默认分隔符是-
补全脚本command -- --completion [shell]为CLI生成补全脚本。
追踪command -- --trace获取命令的Fire追踪信息。
详细输出command -- --verbose

请注意,标志与Fire命令之间通过孤立的--参数进行分隔。
帮助是个例外;获取帮助时孤立的--是可选的。

调用fire.Fire()的参数
参数用法备注
componentfire.Fire(component)如果省略,则默认为所有局部和全局变量的字典。
commandfire.Fire(command='hello --name=5')可以是一个字符串或一个参数列表。如果提供的是字符串,则对其进行拆分以确定参数。如果提供的是列表或元组,则它们就是参数。如果省略command,则默认使用sys.argv[1:](即命令行中的参数)。
namefire.Fire(name='tool')CLI的名称,理想情况下是用户运行CLI时输入的名称。此名称将用于CLI的帮助页面中。如果省略该参数,则会自动推断。
serializefire.Fire(serialize=custom_serializer)如果省略,则简单的类型通过其内置的str方法进行序列化,定义了自定义__str__方法的任何对象通过该方法进行序列化。如果指定了,所有对象都将通过提供的方法进行文本序列化。
不修改任何代码即可使用Fire CLI

您可以在不修改模块代码的情况下使用Python Fire。
语法如下:

python -m fire <module> <arguments>

或者

python -m fire <filepath> <arguments>

例如,python -m fire calendar -h 将把内置的calendar模块当作CLI处理并提供其帮助信息。

示例

这个程序将包括自定义序列化、构造函数参数传递、函数参数传递、以及对*varargs**kwargs的支持。此外,它还将展示如何处理不同类型的输入数据,包括布尔值。

import fire

class Building:
    def __init__(self, name, stories=1, floors_per_story=10):
        self.name = name
        self.stories = stories
        self.floors_per_story = floors_per_story

    def __str__(self):
        return f"{self.name} has {self.stories} stories and {self.floors_per_story} floors per story."

    def climb_stairs(self, stairs_per_floor=10):
        """
        Climb through each floor of each story in the building.
        Yields each step climbed, and messages at the end of each story and the entire building.
        """
        for story in range(self.stories):
            for floor in range(self.floors_per_story):
                for stair in range(stairs_per_floor):
                    yield stair
                yield 'Phew!'  # At the end of each floor
            yield 'Done with this story!'  # At the end of each story
        yield 'Done with the whole building!'  # At the end of the building

def order_by_length(*items, reverse=False):
    """
    Orders items by length, breaking ties alphabetically.
    Optionally reverses the sort order.
    """
    sorted_items = sorted(items, key=lambda item: (len(str(item)), str(item)), reverse=reverse)
    return ' '.join(sorted_items)

def type_of(obj):
    """
    Prints the type of the given object.
    """
    return type(obj).__name__

if __name__ == '__main__':
    fire.Fire({
        'building': Building,
        'order': order_by_length,
        'type': type_of
    })
使用说明
  1. 实例化建筑并爬楼梯:

    • 创建一个建筑实例并指定参数。
    • 调用climb_stairs函数并指定楼梯每层的数量。
    • 例如:
      $ python example.py building --name="Sherrerd Hall" --stories=3 --floors_per_story=5 climb_stairs --stairs_per_floor=10
      
  2. 按长度排序项目:

    • 传递多个项目并可选择性地反转排序。
    • 例如:
      $ python example.py order cat dog elephant --reverse
      
  3. 获取对象类型:

    • 传递一个对象以确定其类型。
    • 例如:
      $ python example.py type [1, 2, 3]
      
示例运行
实例化建筑并爬楼梯
$ python example.py building --name="Sherrerd Hall" --stories=3 --floors_per_story=5 climb_stairs --stairs_per_floor=10
0
1
2
3
4
5
6
7
8
9
Phew!
0
1
2
3
4
5
6
7
8
9
Phew!
0
1
2
3
4
5
6
7
8
9
Phew!
Done with this story!
0
1
2
3
4
5
6
7
8
9
Phew!
0
1
2
3
4
5
6
7
8
9
Phew!
Done with this story!
0
1
2
3
4
5
6
7
8
9
Phew!
Done with the whole building!
按长度排序项目
$ python example.py order cat dog elephant --reverse
elephant dog cat
获取对象类型
$ python example.py type "(1, 2)"
tuple

4. 性能对比

特性/工具optparseargparseclickfire
标准库
易用性简单中等简单非常简单
功能性基础高级高级基础至高级
可读性良好良好非常好良好
维护状态不推荐使用推荐使用活跃维护活跃维护
社区支持有限广泛广泛逐渐增长
  • optparse由于功能限制和已弃用的状态,不推荐新项目使用。
  • argparse是当前的标准选择,提供了强大的功能和良好的用户支持。
  • click以其简洁和声明式的方式,适合快速开发和轻量级应用。
  • fire以其简单易用,适合快速创建基于函数的命令行工具。

分析

  • 维护状态optparse已弃用,不推荐使用。argparse是标准库的一部分,持续得到维护和更新。clickfire都是活跃的第三方库,有持续的更新和社区支持。
  • 学习曲线optparseargparse相对容易上手,但argparse提供了更多的功能和灵活性。click通过装饰器和上下文管理简化了CLI的开发,而fire则提供了一个极简的API。
  • 功能argparseclickfire都提供了创建复杂CLI的能力,包括参数、选项和子命令的支持。optparse在这方面较为有限。
  • 社区和文档clickargparse有较好的社区支持和文档。fire虽然是由Google开发,但其文档和社区支持也相对完善。

结论

  • 对于新项目,推荐使用argparseclickfireargparse作为标准库的一部分,具有稳定性和广泛的支持。clickfire则提供了更现代和灵活的CLI开发方式。
  • optparse由于其已弃用的状态,不推荐用于新项目。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值