构建自定义命令行工具 - 打造专属指令体
前言
在日常开发工作中,我们经常需要重复执行一些特定的操作,比如批量处理文件、自动化部署、数据处理等。如果能够构建一个属于自己的命令行工具,通过简短的指令快速执行这些任务,将大大提升工作效率。
本文将手把手教你如何从零开始构建一个可扩展的自定义命令行工具,实现自己的指令体系。
技术选型
- 语言: Python 3.x (简单易用,跨平台)
- 核心特性:
- 命令解析与分发
- 插件化指令扩展
- 参数处理
- 友好的帮助信息
完整源码实现
主程序 mycli.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
自定义命令行工具 - MyCLI
作者: Your Name
日期: 2025-10-21
"""import sys
import os
from typing import Dict, Callable, List
import json
from datetime import datetimeclass CommandRegistry:"""命令注册器 - 管理所有自定义指令"""def __init__(self):self.commands: Dict[str, Callable] = {}self.descriptions: Dict[str, str] = {}def register(self, name: str, description: str):"""装饰器: 注册命令"""def decorator(func: Callable):self.commands[name] = funcself.descriptions[name] = descriptionreturn funcreturn decoratordef execute(self, command: str, args: List[str]):"""执行命令"""if command in self.commands:try:return self.commands[command](args)except Exception as e:print(f"❌ 执行错误: {e}")return Falseelse:print(f"❌ 未知命令: {command}")print("💡 使用 'help' 查看所有可用命令")return Falsedef show_help(self):"""显示帮助信息"""print("\n" + "="*50)print("🚀 MyCLI - 自定义命令行工具")print("="*50)print("\n📋 可用命令:\n")for name, desc in self.descriptions.items():print(f" {name:<15} - {desc}")print("\n" + "="*50)print("💡 使用方法: mycli <命令> [参数...]")print("="*50 + "\n")# 创建全局命令注册器
registry = CommandRegistry()# ==================== 定义自定义命令 ====================@registry.register("hello", "打招呼命令")
def cmd_hello(args: List[str]):"""打招呼"""name = args[0] if args else "World"print(f"👋 Hello, {name}!")return True@registry.register("time", "显示当前时间")
def cmd_time(args: List[str]):"""显示当前时间"""now = datetime.now()print(f"⏰ 当前时间: {now.strftime('%Y-%m-%d %H:%M:%S')}")return True@registry.register("calc", "简单计算器 (支持: +, -, *, /)")
def cmd_calc(args: List[str]):"""计算器"""if len(args) < 3:print("❌ 用法: calc <数字1> <操作符> <数字2>")print(" 示例: calc 10 + 5")return Falsetry:num1 = float(args[0])operator = args[1]num2 = float(args[2])operations = {'+': lambda x, y: x + y,'-': lambda x, y: x - y,'*': lambda x, y: x * y,'/': lambda x, y: x / y if y != 0 else None}if operator not in operations:print(f"❌ 不支持的操作符: {operator}")return Falseresult = operations[operator](num1, num2)if result is None:print("❌ 错误: 除数不能为0")return Falseprint(f"🧮 计算结果: {num1} {operator} {num2} = {result}")return Trueexcept ValueError:print("❌ 错误: 请输入有效的数字")return False@registry.register("file", "文件操作 (list|create|delete)")
def cmd_file(args: List[str]):"""文件操作"""if not args:print("❌ 用法: file <操作> [参数]")print(" 操作: list, create <文件名>, delete <文件名>")return Falseoperation = args[0]if operation == "list":print("📁 当前目录文件列表:")files = os.listdir('.')for i, file in enumerate(files, 1):icon = "📂" if os.path.isdir(file) else "📄"print(f" {i}. {icon} {file}")return Trueelif operation == "create" and len(args) > 1:filename = args[1]try:with open(filename, 'w', encoding='utf-8') as f:f.write(f"# 创建于 {datetime.now()}\n")print(f"✅ 文件已创建: {filename}")return Trueexcept Exception as e:print(f"❌ 创建失败: {e}")return Falseelif operation == "delete" and len(args) > 1:filename = args[1]try:if os.path.exists(filename):os.remove(filename)print(f"✅ 文件已删除: {filename}")return Trueelse:print(f"❌ 文件不存在: {filename}")return Falseexcept Exception as e:print(f"❌ 删除失败: {e}")return Falseelse:print("❌ 无效的操作")return False@registry.register("note", "笔记管理 (add|list|clear)")
def cmd_note(args: List[str]):"""简单笔记管理"""note_file = ".mycli_notes.json"# 加载笔记def load_notes():if os.path.exists(note_file):with open(note_file, 'r', encoding='utf-8') as f:return json.load(f)return []# 保存笔记def save_notes(notes):with open(note_file, 'w', encoding='utf-8') as f:json.dump(notes, f, ensure_ascii=False, indent=2)if not args:print("❌ 用法: note <操作> [内容]")print(" 操作: add <内容>, list, clear")return Falseoperation = args[0]if operation == "add" and len(args) > 1:notes = load_notes()content = ' '.join(args[1:])note_entry = {"id": len(notes) + 1,"content": content,"time": datetime.now().strftime('%Y-%m-%d %H:%M:%S')}notes.append(note_entry)save_notes(notes)print(f"✅ 笔记已添加 (ID: {note_entry['id']})")return Trueelif operation == "list":notes = load_notes()if not notes:print("📝 暂无笔记")else:print(f"📝 共有 {len(notes)} 条笔记:\n")for note in notes:print(f" [{note['id']}] {note['time']}")print(f" {note['content']}\n")return Trueelif operation == "clear":if os.path.exists(note_file):os.remove(note_file)print("✅ 所有笔记已清空")return Trueelse:print("❌ 无效的操作")return False@registry.register("env", "显示环境变量")
def cmd_env(args: List[str]):"""显示环境变量"""if args:# 显示特定环境变量var_name = args[0]value = os.environ.get(var_name)if value:print(f"🔧 {var_name} = {value}")else:print(f"❌ 环境变量不存在: {var_name}")else:# 显示所有环境变量print("🔧 环境变量列表:\n")for key, value in sorted(os.environ.items()):print(f" {key} = {value}")return True@registry.register("help", "显示帮助信息")
def cmd_help(args: List[str]):"""显示帮助"""registry.show_help()return True@registry.register("exit", "退出程序")
def cmd_exit(args: List[str]):"""退出"""print("👋 再见!")sys.exit(0)# ==================== 主程序 ====================def main():"""主函数"""print("\n🚀 欢迎使用 MyCLI!")print("💡 输入 'help' 查看所有命令,输入 'exit' 退出\n")# 交互模式if len(sys.argv) == 1:while True:try:user_input = input("MyCLI> ").strip()if not user_input:continueparts = user_input.split()command = parts[0]args = parts[1:] if len(parts) > 1 else []registry.execute(command, args)except KeyboardInterrupt:print("\n\n👋 再见!")breakexcept EOFError:print("\n\n👋 再见!")break# 命令行模式else:command = sys.argv[1]args = sys.argv[2:] if len(sys.argv) > 2 else []registry.execute(command, args)if __name__ == "__main__":main()
实现步骤详解
步骤 1: 设计命令注册器
命令注册器是整个系统的核心,负责:
- 注册命令与对应的处理函数
- 存储命令描述信息
- 分发和执行命令
class CommandRegistry:def __init__(self):self.commands = {} # 命令字典self.descriptions = {} # 描述字典
步骤 2: 使用装饰器注册命令
通过装饰器模式,让命令注册变得优雅简洁:
@registry.register("hello", "打招呼命令")
def cmd_hello(args):print(f"Hello, {args[0] if args else 'World'}!")
步骤 3: 实现命令执行逻辑
命令执行器负责:
- 查找命令
- 调用对应处理函数
- 处理异常情况
步骤 4: 添加交互模式
支持两种运行模式:
- 交互模式: 直接运行程序,进入命令提示符
- 命令行模式:
python mycli.py <命令> [参数]
使用示例
1. 启动交互模式
python mycli.py
输出:
🚀 欢迎使用 MyCLI!
💡 输入 'help' 查看所有命令,输入 'exit' 退出MyCLI>
2. 查看帮助
MyCLI> help
3. 使用计算器
MyCLI> calc 100 + 50
🧮 计算结果: 100.0 + 50.0 = 150.0
4. 文件操作
MyCLI> file list
📁 当前目录文件列表:1. 📄 mycli.py2. 📂 docsMyCLI> file create test.txt
✅ 文件已创建: test.txt
5. 笔记管理
MyCLI> note add 明天要完成项目文档
✅ 笔记已添加 (ID: 1)MyCLI> note list
📝 共有 1 条笔记:[1] 2025-10-21 15:30:00明天要完成项目文档
6. 命令行模式
python mycli.py hello Python
👋 Hello, Python!python mycli.py time
⏰ 当前时间: 2025-10-21 15:30:00
扩展建议
1. 添加配置文件支持
import configparserdef load_config():config = configparser.ConfigParser()config.read('mycli.conf')return config
2. 添加日志功能
import logginglogging.basicConfig(filename='mycli.log',level=logging.INFO,format='%(asctime)s - %(levelname)s - %(message)s'
)
3. 实现插件系统
# plugins/my_plugin.py
@registry.register("mycommand", "我的自定义命令")
def my_command(args):print("这是我的插件命令!")
4. 添加命令别名
class CommandRegistry:def __init__(self):self.aliases = {'h': 'help','q': 'exit','ls': 'file list'}
5. 参数验证
from typing import List, Optionaldef validate_args(args: List[str], min_args: int = 0, max_args: Optional[int] = None) -> bool:if len(args) < min_args:return Falseif max_args and len(args) > max_args:return Falsereturn True
项目优化
性能优化
- 懒加载: 只在使用时加载命令模块
- 缓存: 缓存频繁访问的数据
- 异步处理: 对于耗时操作使用异步
用户体验优化
- 命令补全: 集成 readline 库实现 Tab 补全
- 彩色输出: 使用 colorama 库美化输出
- 进度条: 使用 tqdm 显示长任务进度
# 命令补全示例
import readlinedef completer(text, state):options = [cmd for cmd in registry.commands.keys() if cmd.startswith(text)]return options[state] if state < len(options) else Nonereadline.set_completer(completer)
readline.parse_and_bind('tab: complete')
部署与分发
1. 创建可执行脚本
chmod +x mycli.py
2. 添加到系统 PATH
# Linux/Mac
export PATH=$PATH:/path/to/mycli# Windows
setx PATH "%PATH%;C:\path\to\mycli"
3. 创建 setup.py 打包
from setuptools import setupsetup(name='mycli',version='1.0.0',py_modules=['mycli'],entry_points={'console_scripts': ['mycli=mycli:main',],},
)
安装:
pip install -e .
总结
通过本文,我们完整实现了一个可扩展的自定义命令行工具。主要特点:
✅ 模块化设计: 命令注册器 + 装饰器模式
✅ 易于扩展: 添加新命令只需一个装饰器
✅ 双模式支持: 交互模式 + 命令行模式
✅ 友好交互: 清晰的提示和错误处理
✅ 实用功能: 计算器、文件操作、笔记管理等
这个框架可以根据你的需求无限扩展,打造属于自己的效率工具集!