第 92 课 - 项目实战:开发一个命令行工具(CLI)
学习目标
- 理解命令行工具(CLI)的作用和应用场景
- 学会使用 Python 标准库
argparse解析命令行参数 - 掌握
click库的基本用法,创建更优雅的命令行接口 - 能够为你的工具设计清晰的命令、选项和帮助信息
- 了解如何将 Python 脚本打包成一个可分发的命令行程序
核心概念
什么是命令行工具(CLI)?
CLI 是一种通过文本命令与计算机程序交互的界面。与图形界面(GUI)不同,CLI 允许用户通过键盘输入指令,特别适合自动化任务、批量处理和系统管理。
想象一下,你有一个经常需要执行的任务,比如"将当前目录下所有 .txt 文件转换为大写",你可以写一个 Python 脚本来做这件事。但每次都要修改脚本参数、或者执行 python script.py --input_dir . --output_dir converted 就太麻烦了。CLI 工具就是让你直接输入 convert-text --dir . --output converted 就能完成任务的利器。
为什么要使用 argparse 和 click?
argparse:Python 标准库自带,无需安装。适合构建结构清晰、参数复杂的工具,能自动生成帮助文档。click:第三方库,设计更现代、更 Pythonic。用装饰器定义命令和选项,代码更简洁,功能更强大(如支持嵌套命令、自动补全)。
选择建议:简单脚本用 argparse;需要复杂交互、多层命令、美观输出的工具用 click。
代码示例
示例 1:使用 argparse 创建一个简单的文件信息查看工具
#!/usr/bin/env python3
# file_info_cli.py
import argparse
import os
from pathlib import Path
def get_file_info(file_path):
"""获取文件的基本信息"""
path = Path(file_path)
if not path.exists():
return f"错误:文件 '{file_path}' 不存在"
size = path.stat().st_size
modified_time = path.stat().st_mtime
return {
'name': path.name,
'extension': path.suffix,
'size_bytes': size,
'size_human': f"{size/1024:.2f} KB" if size > 1024 else f"{size} Bytes",
'modified': modified_time
}
def main():
# 创建解析器
parser = argparse.ArgumentParser(
description="一个简单的文件信息查看工具",
epilog="示例:python file_info_cli.py myfile.txt --verbose"
)
# 添加位置参数(必填)
parser.add_argument("file", help="要查看的文件路径")
# 添加可选参数
parser.add_argument("-v", "--verbose", action="store_true",
help="显示详细信息")
parser.add_argument("--json", action="store_true",
help="以JSON格式输出")
# 解析命令行参数
args = parser.parse_args()
# 获取文件信息
info = get_file_info(args.file)
# 根据参数输出不同格式
if isinstance(info, str): # 出错信息
print(info)
return
if args.json:
import json
print(json.dumps(info, indent=2))
elif args.verbose:
print(f"文件详情:")
print(f" 名称:{info['name']}")
print(f" 扩展名:{info['extension']}")
print(f" 大小:{info['size_human']} ({info['size_bytes']} 字节)")
print(f" 最后修改:{info['modified']}")
else:
print(f"{info['name']}: {info['size_human']}")
if __name__ == "__main__":
main()
运行示例:
# 基本使用
python file_info_cli.py myfile.txt
# 显示详细信息
python file_info_cli.py myfile.txt --verbose
# 以JSON格式输出
python file_info_cli.py myfile.txt --json
# 查看帮助
python file_info_cli.py --help
示例 2:使用 click 重写上面的工具(更简洁)
#!/usr/bin/env python3
# file_info_click.py
import click
import os
from pathlib import Path
import json
@click.command()
@click.argument('file') # 位置参数
@click.option('--verbose', '-v', is_flag=True, help='显示详细信息')
@click.option('--json', '-j', is_flag=True, help='以JSON格式输出')
def file_info(file, verbose, json):
"""一个简单的文件信息查看工具(Click版)"""
path = Path(file)
if not path.exists():
click.echo(click.style(f"错误:文件 '{file}' 不存在", fg='red'))
return
info = {
'name': path.name,
'extension': path.suffix,
'size_bytes': path.stat().st_size,
'size_human': f"{path.stat().st_size/1024:.2f} KB" if path.stat().st_size > 1024 else f"{path.stat().st_size} Bytes",
'modified': path.stat().st_mtime
}
if json:
click.echo(json.dumps(info, indent=2))
elif verbose:
click.echo(f"文件详情:")
click.echo(f" 名称:{info['name']}")
click.echo(f" 扩展名:{info['extension']}")
click.echo(f" 大小:{info['size_human']} ({info['size_bytes']} 字节)")
click.echo(f" 最后修改:{info['modified']}")
else:
click.echo(f"{info['name']}: {info['size_human']}")
if __name__ == '__main__':
file_info()
Click 的优势:
- 用装饰器清晰定义参数和选项
- 自动处理类型转换和验证
- 代码更简洁易读
- 内置更好的帮助文档格式
实践练习
练习 1:基础任务(基于 argpars)
创建一个 word_counter.py 命令行工具,要求:
- 接受一个文件名作为必选参数
- 可选参数
-w统计单词数(默认) - 可选参数
-l统计行数 - 可选参数
-c统计字符数 - 同时使用时显示所有统计结果
预期输出:
$ python word_counter.py story.txt -w -l
story.txt:
单词数: 1234
行数: 56
$ python word_counter.py story.txt -c
story.txt:
字符数: 6789
练习 2:进阶任务(使用 click)
使用 click 创建一个 file_manager.py 工具,实现多子命令:
ls- 列出目录内容(类似ls -l)mkdir- 创建目录count- 统计目录中的文件数量- 每个子命令都有相应的选项
预期输出:
$ python file_manager.py ls --dir . --all
总计 10 项
drwxr-xr-x 5 user staff 160 May 10 10:00 .
drwxr-xr-x 3 user staff 96 May 10 09:00 ..
-rw-r--r-- 1 user staff 234 May 10 10:01 file1.txt
$ python file_manager.py mkdir new_folder
创建目录: new_folder
$ python file_manager.py count --dir ./documents
./documents 中有 15 个文件
练习 3:打包分发
将你完成的 CLI 工具打包,使得用户可以通过 pip install 安装后直接使用命令,而不是 python script.py。
要求:
- 创建
setup.py或pyproject.toml - 配置
entry_points或scripts - 实现安装后直接使用
fileinfo命令(而不是python file_info_cli.py)
常见错误
1. 参数顺序错误
# 错误:位置参数必须在可选参数之前
parser.add_argument("--output", help="输出文件")
parser.add_argument("input_file", help="输入文件") # 这会导致错误
# 正确:先定义位置参数,再定义可选参数
parser.add_argument("input_file", help="输入文件")
parser.add_argument("--output", help="输出文件")
2. 忘记处理必选参数缺失
# 如果用户没有提供必选参数,argparse会自动报错,但click需要手动处理
@click.command()
@click.argument('filename')
def read_file(filename):
# 如果没有这个检查,click会报错
if not os.path.exists(filename):
click.echo(f"文件 {filename} 不存在")
return
3. 混淆参数和选项
# 错误:把位置参数当作选项来定义
parser.add_argument("--file", required=True) # 这样需要用户输入 --file value
# 正确:位置参数不需要前缀
parser.add_argument("file") # 用户直接输入文件名
# 如果确实是可选参数,但需要必填,使用required=True
parser.add_argument("--config", required=True)
4. 没有提供帮助信息
# 不要这样(没有帮助信息)
parser.add_argument("file")
# 应该这样(清晰的帮助信息)
parser.add_argument("file", help="要处理的输入文件路径")
小结
关键要点回顾:
- CLI工具的价值:将常用操作封装成命令,提高工作效率,便于自动化和脚本集成
- 选择合适的库:
- 简单需求用
argparse(标准库,无需安装) - 复杂项目用
click(更现代,功能更强大)
- 简单需求用
- 设计良好的CLI:
- 提供清晰的帮助文档
- 合理的参数和选项设计
- 良好的错误提示
- 实战经验:
- 先从简单工具开始
- 逐步添加功能(输出格式、颜色、进度条等)
- 考虑可扩展性和可维护性
- 分发你的工具:
- 使用
setup.py打包 - 通过
entry_points创建可执行命令 - 发布到 PyPI 让他人使用
- 使用
下一步学习方向:
- 学习
click的高级功能:嵌套命令、命令组、自动补全 - 探索更复杂的CLI应用:进度条、彩色输出、交互式提示
- 研究优秀的开源CLI工具(如
httpie、black、pytest)的设计 - 学习如何为你的工具创建文档网站
记住:好的CLI工具应该像一位贴心的助手——用最少的交互完成最多的工作,并在出错时给出清晰的指导。
练习编辑器
python
Loading...