命令行模块——click(二)
1.Click命令
命令是 Click 库中的核心部分,它们代表了我们可以在命令行中执行的不同操作。在 Click 中,我们可以通过使用 @click.command()
装饰器来定义一个命令,并使用函数体内的代码来实现该命令的功能。例如:
import click
@click.command()
def hello():
click.echo("Hello, world!")
在上面的例子中,我们使用 Click 的 @click.command()
装饰器来定义了一个名为 hello
的命令,该命令不接收任何参数,如果代码写在demo.py当执行python demo.py
时,会输出“Hello, world!”的信息。一般来说,我们会给命令添加参数和选项,也就是在之前的文章中所写的那样。
2.Click命令组
命令组是 Click 库中用于组织一组相关命令的工具。它允许将一组命令作为一个整体进行操作,并且可以方便地进行嵌套和组合。在 Click 中,我们可以通过 click.group()
装饰器来定义一个命令组,并使用 @click.command()
装饰器来定义该组内的命令。
下面是一个使用命令组的示例,其中我们定义了一个名为 cli
的命令组,并将两个子命令 init
和 status
添加到该组中:
import click
@click.group()
def cli():
pass
@cli.command()
def init():
click.echo("Initialized the database")
@cli.command()
def status():
click.echo("Status of the database")
if __name__ == "__main__":
cli()
当我们命令窗执行python demo.py init
输出为:Initialized the database
。在上面的代码中,我们首先使用 click.group()
装饰器定义了一个名为 cli
的命令组,然后使用 command()
方法为该组添加了两个子命令 init
和 status
。当用户在命令行中输入 python demo.py
时,程序会默认执行 cli
命令组中的逻辑,并按需调用子命令。
多组命令的情况:一个模块中包含多个命令组,也就是模块中定义多个装饰了 click.group()
的函数,每个函数对应一个命令组。同时,也可以在每个命令组下面定义多个子命令,使用 @click.command()
装饰器来为命令组添加子命令。
更加复杂的情况:
import click
@click.group()
def cli():
"""Main tool"""
@cli.group()
def cmd():
"""Command sub-group"""
@cmd.command()
def init():
"""Initialize"""
print('Init')
@cli.group()
def config():
"""Configuration sub-group"""
@config.group()
def network():
"""Network configuration"""
@network.command()
def ip():
"""Set IP address"""
print('Set IP')
@network.command()
def subnet():
"""Set subnet mask"""
print('Set subnet mask')
def main():
cli()
# cmd()
# config()
# network()
if __name__ == '__main__':
main()
在这个示例中,我们定义了一个命令行接口,包含:- 命令组cli作为主入口
- cli下面有两个子命令组cmd和config
- cmd下面有一个子命令init
- config下面又有一个子命令组network
- network下面有两个子命令ip和subnet所以整个命令行接口的结构如下:
这里调用的时候就用注意啦!从以上的结构上可以看出来,命令cmd和config是依附于cli的,所以在main中调用了cli,在命令窗中输入:python demo.py config network subnet
,可以看到不用在写上cli,cli是默认执行的,只需要指明器子命令,子命令下的子命令即可。这就引发了我的好奇,是不是我在main中调用cmd和config可以默认执行呢?所以进行了尝试,在命令窗中运行了:python demo.py init
,报错Error: No such command 'init'
,看来就算在main中直接吊用了cmd,cmd也不是像cli那样默认执行的,这个要注意。其他情况也是同理,就算是在main中调用了config也不能直接输入python demo.py network subnet
,否则报错,调用subnet需要python demo.py config network subnet
。
再举一个例子说明一下:
import click
@click.group()
def cli1():
"""Main command line tool"""
@cli1.group()
def cli2():
"""Sub command group"""
@cli2.command()
def init():
"""Initialize"""
print('Init')
@cli1.group()
def cli3():
"""Other sub command group"""
@cli3.command()
def add():
"""Add item"""
print('Add')
def main():
cli1()
cli2()
cli3()
if __name__ == '__main__':
main()
在这个示例中,我们定义了:- 命令组cli1作为根命令组
- cli1下面有两个子命令组cli2和cli3
- cli2下面有子命令init
- cli3下面有子命令add
在main函数中,我们按嵌套结构注册了所有命令组:cli1 -> cli2 -> cli3
所以在命令行可以这样调用:
python file.py
# 只调用cli1根命令组,等待子命令
python file.py cli2 init
# cli1 -> cli2 -> init
输出:Init
python file.py cli3 add
# cli1 -> cli3 -> add
输出:Add
以上就是命令组的一些知识梳理。
还有一点我在代码中遇到的这里补充一下。一个与click有关的装饰器@xx.result_callback()
,其中xx是定义好的函数。具体举一个例子来说明一下这个装饰器的用法和作用。
import click
@click.group()
# @click.option("--a", type=int)
# @click.option("--b", type=int)
@click.argument("a", type=int)
@click.argument("b", type=int)
def cli(**kwargs):
pass
@cli.result_callback()
def add(haha, a, b):
c = a+b
print("Welcome!!!", c)
@cli.command()
def end():
click.echo("the end")
if __name__ == "__main__":
cli()
以上例子中@cli.result_callback(show_result)
用于为命令定义结果回调函数。回调函数的就是不用专门调用,执行的时候是默认的。这里在学习它的时候遇到了一些问题,这里记录一下。首先使用回调函数的时候使用的是@click.group()
装饰器,放在主函数前,其次,要回调的函数要是使用@xx.result_callback()
,其中xx表示主函数名字,还用回调函数中的参数要在主函数前就用@click.option
和@click.argument
就定义好,如果在回调函数前定义,会报错。最后,主函数要有子命令这个代码的子命令是end函数,使用前要用@cli.command()
装饰器(如果没有写子命令,会报错)。回调函数中的参数是@click.option
和@click.argument
定义好的,其实最开始这个回调程序我是这样写的:
def add(a, b):
c = a+b
print("Welcome!!!", c)
调用时python demo.py 2 20 end
报错:TypeError: add() got multiple values for argument 'a'
逻辑是对的为啥会出现这样的错误,很不理解。就查,但是跟这个关系都不太大。既然说是参数有问题,就鬼使神差的,我就这个函数中加了一个参数(这个参数之前也没有定义),就不报错了,但是不知道为啥。反正下次再使用回调函数的时候,再用出现这样的错误就知道咋改了。
以上就是我用到的关于click命令模块之后总结的东西。