Python CLI 程序

    随着AI的不断发展,CLI因为无GUI交互界面,直接传入参数,即可完成很多任务,非常方便,今天一起来学习在Python中如何开发CLI程序。

1.使用 sys.argv

    利用 Python 自带的sys.argv 来快速传递参数,适用比较简单的程序且无需要记住参数名称的功能,但有一个缺点就是,参数得按顺序来进行传递。示例如下所示:

import sys

def add(a: int, b: int) -> int:
    return int(a) + int(b)


if __name__ == '__main__':
    if len(sys.argv) > 2:
        a, b = sys.argv[1], sys.argv[2]
        print(f"a+b={add(a, b)}")

    运行结果如下所示:

$ uv run python sys_args.py 12 24
a+b=36

2.使用 argparse

    这是Python自带的标准库,无须安装第三方依赖,功能相比于 sys.args 要更多。示例如下所示:

import argparse


def argparse_demo():
    # 定义一个 ArgumentParse 实例
    parse=argparse.ArgumentParser(
        # 程序名称
        prog="python-cli-demo",
        # 使用说明
        usage="python-cli-demo [command] [flag]",
        # 描述信息
        description="python-cli-demo 使用示例",
        # 说明信息
        epilog="Copyright(R) 2026 By Surpass"
    )
    # add_argument() 用于指定程序将能接受哪些命令行选项

    # 定义位置参数
    parse.add_argument("login",help="连接数据库")

    # 定义关键字参数,数据类型为字符串类型
    parse.add_argument("--ip",default="127.0.0.1",type=str,help="IP地址")
    # 定义关键字参数,并允许用户输入短命名的参数
    parse.add_argument("--port",default=3306,type=int,help="连接端口")
    parse.add_argument("-u","--user",type=str,required=True,help="用户名")
    parse.add_argument("-p","--password",type=str,required=True,help="密码")
    parse.add_argument("-d","--database",default="demo_database",type=str,help="访问目标数据库")

    # 表明该参数只有两个值 true 和 false ,如果指定了该项,则表示其值为 true ,未指定则表示为 false
    parse.add_argument("-c","--compress",action="store_true",required=False,help="是否启用压缩模式")

    # 给参数限定可选范围值
    parse.add_argument("-v","--verbose",type=int,choices=[1,2,3],help="显示详细信息的级别,越大越详细")

    # 解析参数
    args=parse.parse_args()

    # 打印传入的参数信息
    arg_output_str=f"python-cli-demo {args.login} --ip {args.ip} --port {args.port} -u {args.user} -p {args.password} -d {args.database} -c {args.compress} -v {args.verbose}"

    if (verbose_level:=args.verbose)==1:
        print("当前显示详细级别为1")
    elif verbose_level ==2:
        print("当前显示详细级别为2")
    elif verbose_level==3:
        print("当前显示详细级别为3")
    else:
        print("显示详细级别输入错误")


    print(f"DEBUG:连接字符串信息为:{arg_output_str}")

    print("调用登录-Demo ...")

    login_demo(ip=args.ip,port=args.port,user=args.user,password=args.password,database=args.database,is_compress=args.compress)


def login_demo(
        ip:str,
        port:int,
        user:str,
        password:str,
        database:str,
        is_compress:bool
):
    if is_compress:
        print(f"连接数据库,启用压缩模式,连接信息为:{user}:{password}@{ip}:{port} {database} {is_compress}")
        return
    print(f"连接数据库,未启用压缩模式,连接信息为:{user}:{password}@{ip}:{port} {database} {is_compress}")

if __name__ == "__main__":
    argparse_demo()

    运行结果如下所示:

# 测试第一种情况
$ uv run python std_argparse.py login --ip 192.168.9.10 --port 8080 -u surpass -p password -d demo -c -v 4
usage: python-cli-demo [command] [flag]
python-cli-demo: error: argument -v/--verbose: invalid choice: '4' (choose from '1', '2', '3')

# 测试第二种情况
$ uv run python std_argparse.py login --ip 192.168.9.10 --port 8080 -u surpass -p password -d demo -c -v 2
当前显示详细级别为2
DEBUG:连接字符串信息为:python-cli-demo login --ip 192.168.9.10 --port 8080 -u surpass -p password -d demo -c True -v 2
调用登录-Demo ...
连接数据库,启用压缩模式,连接信息为:surpass:password@192.168.9.10:8080 demo True

# 测试第三种情况
$ uv run python std_argparse.py login --ip 192.168.9.10 --port 8080 -u surpass -p password -d demo -v 2
当前显示详细级别为2
DEBUG:连接字符串信息为:python-cli-demo login --ip 192.168.9.10 --port 8080 -u surpass -p password -d demo -c False -v 2
调用登录-Demo ...
连接数据库,未启用压缩模式,连接信息为:surpass:password@192.168.9.10:8080 demo False

    由于 argparse 是系统自带,依赖最少,参数解析能力完整,同时也支持 subcommand,可以满足一般性的要求。

3.使用 click 框架

    click 是一款用于创建 CLI 程序的第三方框架,适合于存在多级命令参数和选项很复杂CLI有很多交互逻辑高度可配置性的场景,click 官网地址

3.1 安装

    可以使用pip安装或uv安装,如下所示:

pip install click
uv add click

3.2 使用方法

    click 使用方式非常简单,通常分为两步:

  • 使用@click.command()装饰函数,使其成为CLI接口
  • 使用@click.option()装饰函数,为其添加CLI选项

3.2.1 基本用法

import click

@click.command(name="重复打印某字符串")
@click.option("-c","--count",default=1,help="重复的次数")
@click.option("-s","--string",prompt="要重复的字符串",help="需要重复打印的字符串")
def repeat(string,count):
    for i in range(1,count+1):
        click.echo(f"repeate:{i} - {string}")

if __name__ == "__main__":
    repeat()

    运行结果如下所示:

# 测试第一种情况
$ uv run python click_demo.py -c 3 -s Surpass
repeate:1 - Surpass
repeate:2 - Surpass
repeate:3 - Surpass

# 测试第二种情况:交互式
$ uv run python click_demo.py -c 3 
要重复的字符串: Surpass
repeate:1 - Surpass
repeate:2 - Surpass
repeate:3 - Surpass

# 测试第三种情况
$ uv run python click_demo.py 
要重复的字符串: Surpass
repeate:1 - Surpass

# 查看帮助
$ uv run python click_demo.py --help
Usage: click_demo.py [OPTIONS]

Options:
  -c, --count INTEGER  重复的次数
  -s, --string TEXT    需要重复打印的字符串
  --help               Show this message and exit.

这里没有使用 python 自带的 print()。是因为click支持不同版本的 python,为了获得更好的兼容性和提供更丰富的功能(例如支持ANSI字体颜色的支持)。

    click.option 基本用法就是通过指定CLI选项的名称,并从CLI中提取参数值,再将其传递给函数,常用的设置如下所示:

  • default:CLI参数默认值
  • help: 参数说明
  • type: 参数类型
  • prompt:在CLI中没有输入对应的参数时,会根据 prompt 信息提示用户输入

3.2.2 组合命令

    click 可以通过group来创建命令行组,即可以通过各种参数来解决相同类别的不同问题。示例如下所示:

import click

@click.group()
def cli():
    pass

@click.command()
def init_db():
    click.echo("初始化数据库")

@click.command()
def drop_db():
    click.echo("删除数据库")

cli.add_command(init_db)
cli.add_command(drop_db)

if __name__ == "__main__":
    cli()

    运行结果如下所示:

$ uv run python click_group.py  
Usage: click_group.py [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  drop-db
  init-db

$ uv run python click_group.py  init-db
初始化数据库

$ uv run python click_group.py  drop-db
删除数据库

    对于一些比较简单的脚本,也可以使用group.command()自动附加并创建命令。通过修改装饰器,还可以按下这种方式来达到同样的效果。

import click

@click.group()
def cli():
    pass

@cli.command()
def init_db():
    click.echo("初始化数据库")

@cli.command()
def drop_db():
    click.echo("删除数据库")

if __name__ == "__main__":
    cli()

3.2.3 添加参数

    如果需要添加参数,可以使用option()argument装饰器,示例如下所示:

import click

@click.command()
@click.option("-c","--count",default=1,help="重复的次数")
@click.argument("string") # 添加参数
def repeat(string,count):
    for i in range(1,count+1):
        click.echo(f"repeate:{i} - {string}")

if __name__ == "__main__":
    repeat()

    注意运行结果与前面的区别,如下所示:

$ uv run click_args.py 
Usage: click_args.py [OPTIONS] STRING
Try 'click_args.py --help' for help.

Error: Missing argument 'STRING'.

$ uv run click_args.py --help
Usage: click_args.py [OPTIONS] STRING

Options:
  -c, --count INTEGER  重复的次数
  --help               Show this message and exit.

$ uv run click_args.py Surpass -c 4
repeate:1 - Surpass
repeate:2 - Surpass
repeate:3 - Surpass
repeate:4 - Surpass

4.使用 Typer

    Typer 是一个用于构建 CLI 应用程序的库,基于 Python 类型提示,可以运行 python 程序并将其转换为 CLI 应用程序。Typer 官网

4.1 安装

    可以使用pip安装或uv安装,如下所示:

pip install typer
uv add typer

4.2 使用

4.2.1 快速入门

    在 Python 脚本中内部可以不使用 typer ,但可以使用 typer 命令将其转换为CLI应用程序运行

def greeting(name:str):
    print(f"Hello, {name}")

    通过 typer 转换为 CLI 应用程序,运行结果如下所示:

$ uv run typer typer_demo.py run --help
                                                                                 
 Usage: typer [PATH_OR_MODULE] run [OPTIONS] {name}                              
                                                                                 
 Run the provided Typer app.                                                     
                                                                                 
╭─ Arguments ───────────────────────────────────────────────────────────────────╮
│ *    name      <str>  [required]                                              │
╰───────────────────────────────────────────────────────────────────────────────╯
╭─ Options ─────────────────────────────────────────────────────────────────────╮
│ --help          Show this message and exit.                                   │
╰───────────────────────────────────────────────────────────────────────────────╯

$ uv run typer typer_demo.py run  Surpass
Hello, Surpass

    除以上方式,还可以使用以下方式:

import typer


def greeting(name:str):
    print(f"Hello, {name}")


if __name__ == "__main__":
    typer.run(greeting)

    运行结果如下所示:

$ uv run python typer_demo.py  Surpass
Hello, Surpass

4.2.2 参数

    CLI中参数是指定特定顺序传递给CLI应用程序的CLI参数,默认情况,它们是必需的。在前面的示例中,我们已经了解如何添加CLI参数,现在再来看看另一种添加CLI参数的方法,示例如下所示:

import typer
from typing_extensions import Annotated


def greeting(name: Annotated[str, typer.Argument()]):
    print(f"Hello,{name}")


if __name__ == '__main__':
    typer.run(greeting)

    运行结果如下所示:

$ uv run python typer_args.py Surpass
Hello,Surpass

4.2.2.1 可选参数

    要使CLI参数可选,可以使用typer.Argument()将默认值作为第一个参数传递给typer.Argument(),如下所示:

from typing import Optional

import typer
from typing_extensions import Annotated


def greeting(name: Annotated[Optional[str], typer.Argument()]=None):
    if name is None:
        print(f"Hello,World")
    else:
        print(f"Hello,{name}")


if __name__ == '__main__':
    typer.run(greeting)

    运行结果如下所示:

$ uv run python typer_args.py 
Hello,World

$ uv run python typer_args.py Surpass
Hello,Surpass

由于使用了typer.Argument(),typer 就会知道这是一个CLI参数。

4.2.2.2 带默认值参数

    可以使用 typer.Argument() 来设置默认参数值,这样就可以保证CLI参数是可选并具有默认值的。示例如下所示:

import typer
from typing_extensions import Annotated


def greeting(name: Annotated[str, typer.Argument()]="Surpass"):
    print(f"Hello,{name}")


if __name__ == '__main__':
    typer.run(greeting)

    运行结果如下所示:

$ uv run python typer_default_args.py --help
                                                                                 
 Usage: typer_default_args.py [OPTIONS] [name]                                   
                                                                                 
╭─ Arguments ───────────────────────────────────────────────────────────────────╮
│   name      <str>  [default: Surpass]                                         │
╰───────────────────────────────────────────────────────────────────────────────╯
╭─ Options ─────────────────────────────────────────────────────────────────────╮
│ --help          Show this message and exit.                                   │
╰───────────────────────────────────────────────────────────────────────────────╯

$ uv run python typer_default_args.py 
Hello,Surpass

$ uv run python typer_default_args.py Evan
Hello,Evan

4.2.2.3 动态默认值参数

    可以通过函数作为default_factory参数来传递动态默认值,示例如下所示:

import random

import typer
from typing_extensions import Annotated


def get_random_name()->str:
    return random.choice(["Surpass","Evan","Kevin","Alice","Bob"])

def greeting(name: Annotated[str, typer.Argument(default_factory=get_random_name)]):
    print(f"Hello,{name}")


if __name__ == '__main__':
    typer.run(greeting)

    运行结果如下所示:

$ uv run python typer_dynamic_args.py --help
                                                                                 
 Usage: typer_dynamic_args.py [OPTIONS] [name]                                   
                                                                                 
╭─ Arguments ───────────────────────────────────────────────────────────────────╮
│   name      <str>  [default: (dynamic)]                                       │
╰───────────────────────────────────────────────────────────────────────────────╯
╭─ Options ─────────────────────────────────────────────────────────────────────╮
│ --help          Show this message and exit.                                   │
╰───────────────────────────────────────────────────────────────────────────────╯

$ uv run python typer_dynamic_args.py 
Hello,Alice

$ uv run python typer_dynamic_args.py 
Hello,Bob

4.2.3 选项

    CLI中选项是指使用特定名称传递给CLI应用程序的参数,一般情况下,它们是可选的。在 typer 中可以使用typer.Option()来修改CLI选项。

4.2.3.1 基本用法

    这里演示可以在CLI应用程序,使用类似--option-p来传递参数,示例如下所示:

import typer
from typing_extensions import Annotated


def greeting(
        name: Annotated[str, typer.Option("--name", "-n", help="姓名")],
        formal: Annotated[bool, typer.Option("--formal", "-f", help="是否采用正式用法")] = False,
        gender: Annotated[str, typer.Option("--gender", "-g", help="性别")] = "male"
):
    if formal and gender == "male":
        print(f"Good day Mr.{name}")
    elif formal and gender == "female":
        print(f"Good day Ms.{name}")
    else:
        print(f"Hello,{name}")


if __name__ == '__main__':
    typer.run(greeting)

    运行结果如下所示:

$ uv run python typer_name_option.py --help
                                                                                 
 Usage: typer_name_option.py [OPTIONS]                                           
                                                                                 
╭─ Options ─────────────────────────────────────────────────────────────────────╮
│ *  --name    -n      <str>  姓名 [required]                                    │
│    --formal  -f             是否采用正式用法                                     │
│    --gender  -g      <str>  性别 [default: male]                               │
│    --help                   Show this message and exit.                       │
╰───────────────────────────────────────────────────────────────────────────────╯

$ uv run python typer_name_option.py -n Surpass -f -g male
Good day Mr.Surpass

$ uv run python typer_name_option.py -n Surpass -f  -g female
Good day Ms.Surpass

4.2.3.2 交互式用法

import typer
from typing_extensions import Annotated


def greeting(
        name: Annotated[str, typer.Option("--name", "-n", help="姓名")],
        email: Annotated[str, typer.Option("--email", prompt=True, confirmation_prompt=True, help="邮箱")]
):
    print(f"Hello,{name},your email is: {email}")


if __name__ == '__main__':
    typer.run(greeting)

    运行结果如下所示:

$ uv run python typer_interact.py --help
                                                                                 
 Usage: typer_interact.py [OPTIONS]                                              
                                                                                 
╭─ Options ─────────────────────────────────────────────────────────────────────╮
│ *  --name   -n      <str>  姓名 [required]                                     │
│ *  --email          <str>  邮箱 [required]                                     │
│    --help                  Show this message and exit.                         │
╰───────────────────────────────────────────────────────────────────────────────╯

$ uv run python typer_interact.py -n Surpass
Email: surpassme@surpassme.net
Repeat for confirmation: surpassme@surpassme.net
Hello,Surpass,your email is: surpassme@surpassme.net

4.2.3.3 选项回调

import typer
from typing_extensions import Annotated


def name_callback(name: str):
    if name not in ["Surpass", "Evan", "Kevin"]:
        raise typer.BadParameter(f"{name} is not allowed.Only Surpass,Evan,Kevin")
    return name


def greeting(
        name: Annotated[str, typer.Option("--name", "-n", callback=name_callback)]
):
    print(f"Hello,{name}")


if __name__ == '__main__':
    typer.run(greeting)

    运行结果如下所示:

$ uv run python typer_option_callback.py --name Surpass
Hello,Surpass

$ uv run python typer_option_callback.py --name Alice
Usage: typer_option_callback.py [OPTIONS]
Try 'typer_option_callback.py --help' for help.
╭─ Error ───────────────────────────────────────────────────────────────────────╮
│ Invalid value for '--name' / '-n': Alice is not allowed.Only                  │
│ Surpass,Evan,Kevin                                                            │
╰───────────────────────────────────────────────────────────────────────────────╯

4.2.3.4 查看版本

from typing import Optional
import typer
from typing_extensions import Annotated

__version__: str = "1.0.0"


def name_callback(name: str):
    if name not in ["Surpass", "Evan", "Kevin"]:
        raise typer.BadParameter(f"{name} is not allowed.Only Surpass,Evan,Kevin")
    return name


def version_callback(value: bool):
    if value:
        print(f"CLI Version:{__version__}")
        raise typer.Exit()


def greeting(
        name: Annotated[str, typer.Option("--name", "-n", callback=name_callback)],
        version: Annotated[Optional[bool], typer.Option("--version", "-v", callback=version_callback)] = None
):
    print(f"Hello,{name}")


if __name__ == '__main__':
    typer.run(greeting)

    运行结果如下所示:

$ uv run python typer_version.py --version
CLI Version:1.0.0

$ uv run python typer_version.py -v
CLI Version:1.0.0

4.2.4 子命令/命令组

    在CLI命令通常还会存在一个或多个子命令的情况,例如uv run python ...等,这种情况被称之为子命令或命令组。

4.2.4.1 命令存在于多个文件

  • items.py
import typer
from typing_extensions import Annotated

app = typer.Typer()


@app.command()
def create(item: Annotated[str, typer.Option("--item", "-i", help="名称")]):
    print(f"Create item: {item}")


@app.command()
def delete(item: Annotated[str, typer.Option("--item", "-i", help="名称")]):
    print(f"Delete item: {item}")


@app.command()
def sell(item: Annotated[str, typer.Option("--item", "-i", help="名称")]):
    print(f"Sell item: {item}")


if __name__ == '__main__':
    app()

  • user.py
import typer
from typing_extensions import Annotated

app = typer.Typer()


@app.command()
def create(username: Annotated[str, typer.Option("--username", "-u", help="用户名称")]):
    print(f"Create username: {username}")


@app.command()
def delete(username: Annotated[str, typer.Option("--username", "-u", help="用户名称")]):
    print(f"Delete username: {username}")


if __name__ == '__main__':
    app()
  • main.py
import typer

from typer_command_group import items, users

if __name__ == '__main__':
    app = typer.Typer()
    app.add_typer(items.app, name="items")
    app.add_typer(users.app, name="users")

    app()

    运行结果如下所示:

$ uv run python main.py --help
                                                                                 
 Usage: main.py [OPTIONS] COMMAND [ARGS]...                                      
                                                                                 
╭─ Options ─────────────────────────────────────────────────────────────────────╮
│ --install-completion          Install completion for the current shell.       │
│ --show-completion             Show completion for the current shell, to copy  │
│                               it or customize the installation.               │
│ --help                        Show this message and exit.                     │
╰───────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ────────────────────────────────────────────────────────────────────╮
│ items                                                                         │
│ users                                                                         │
╰───────────────────────────────────────────────────────────────────────────────╯

$ uv run python main.py items --help
                                                                                 
 Usage: main.py items [OPTIONS] COMMAND [ARGS]...                                
                                                                                 
╭─ Options ─────────────────────────────────────────────────────────────────────╮
│ --help          Show this message and exit.                                   │
╰───────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ────────────────────────────────────────────────────────────────────╮
│ create                                                                        │
│ delete                                                                        │
│ sell                                                                          │
╰───────────────────────────────────────────────────────────────────────────────╯

$ uv run python main.py items create --help
                                                                                 
 Usage: main.py items create [OPTIONS]                                           
                                                                                 
╭─ Options ─────────────────────────────────────────────────────────────────────╮
│ *  --item  -i      <str>  名称 [required]                                     │
│    --help                 Show this message and exit.                         │
╰───────────────────────────────────────────────────────────────────────────────╯


$ uv run python main.py items create --item Gold
Create item: Gold

$ uv run python main.py users create -u Surpass
Create username: Surpass

4.2.4.2 命令存在单个文件中

import typer
from typing_extensions import Annotated

app = typer.Typer()

items_app = typer.Typer()
user_app = typer.Typer()

app.add_typer(items_app, name="items", help="items管理")
app.add_typer(user_app, name="users", help="用户管理")


@items_app.command(name="create", help="创建item")
def create(item: Annotated[str, typer.Option("--item", "-i", help="名称")]):
    print(f"Create item: {item}")


@items_app.command(name="delete", help="删除item")
def delete(item: Annotated[str, typer.Option("--item", "-i", help="名称")]):
    print(f"Delete item: {item}")


@items_app.command(name="sell", help="售卖item")
def sell(item: Annotated[str, typer.Option("--item", "-i", help="名称")]):
    print(f"Sell item: {item}")


@user_app.command(name="create", help="创建用户")
def create(username: Annotated[str, typer.Option("--username", "-u", help="用户名称")]):
    print(f"Create username: {username}")


@user_app.command(name="delete", help="删除用户")
def delete(username: Annotated[str, typer.Option("--username", "-u", help="用户名称")]):
    print(f"Delete username: {username}")


if __name__ == '__main__':
    app()

    运行结果如下所示:

$ uv run python typer_command_group.py  --help
                                                                                 
 Usage: typer_command_group.py [OPTIONS] COMMAND [ARGS]...                       
                                                                                 
╭─ Options ─────────────────────────────────────────────────────────────────────╮
│ --install-completion          Install completion for the current shell.       │
│ --show-completion             Show completion for the current shell, to copy  │
│                               it or customize the installation.               │
│ --help                        Show this message and exit.                     │
╰───────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ────────────────────────────────────────────────────────────────────╮
│ items  items管理                                                               │
│ users  用户管理                                                                 │
╰───────────────────────────────────────────────────────────────────────────────╯

$ uv run python typer_command_group.py users --help
                                                                                 
 Usage: typer_command_group.py users [OPTIONS] COMMAND [ARGS]...                 
                                                                                 
 用户管理                                                                        
                                                                                 
╭─ Options ─────────────────────────────────────────────────────────────────────╮
│ --help          Show this message and exit.                                    │
╰───────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ────────────────────────────────────────────────────────────────────╮
│ create  创建用户                                                                │
│ delete  删除用户                                                                │
╰───────────────────────────────────────────────────────────────────────────────╯

$ uv run python typer_command_group.py users delete --help
                                                                                 
 Usage: typer_command_group.py users delete [OPTIONS]                            
                                                                                 
 删除用户                                                                        
                                                                                 
╭─ Options ─────────────────────────────────────────────────────────────────────╮
│ *  --username  -u      <str>  用户名称 [required]                               │
│    --help                     Show this message and exit.                      │
╰───────────────────────────────────────────────────────────────────────────────╯

$ uv run python typer_command_group.py users delete -u Surpass
Delete username: Surpass

$ uv run python typer_command_group.py items delete -i Gold
Delete item: Gold
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容