|
|
@@ -1,298 +1,11 @@
|
|
|
-# 实战案例:命令行工具
|
|
|
+# 实战案例:开发一个高效的命令行工具 (CLI)
|
|
|
|
|
|
-> 难度:⭐⭐ 中等 | 预计时间:1-2 小时 | 技术栈:Python + Click
|
|
|
+> 本案例将指导您如何与 AI 协作,构思、设计并实现一个实用的命令行工具,例如一个文件处理器或信息查询工具。
|
|
|
|
|
|
-## 🎯 项目目标
|
|
|
+* **技术栈**: [待定,例如:Python (Typer/Click), Go (Cobra)]
|
|
|
+* **难度**: 初级到中级
|
|
|
+* **预计用时**: 4-6 小时
|
|
|
|
|
|
-构建一个实用的命令行工具,实现:
|
|
|
-- 文件批量重命名
|
|
|
-- 支持多种命名规则
|
|
|
-- 预览和确认机制
|
|
|
-- 彩色输出
|
|
|
+---
|
|
|
|
|
|
-## 📋 开始前的准备
|
|
|
-
|
|
|
-### 环境要求
|
|
|
-- Python 3.8+
|
|
|
-- pip
|
|
|
-
|
|
|
-### 第一步:需求澄清
|
|
|
-
|
|
|
-复制以下提示词给 AI:
|
|
|
-
|
|
|
-```
|
|
|
-我想用 Vibe Coding 的方式开发一个文件批量重命名的命令行工具。
|
|
|
-
|
|
|
-技术要求:
|
|
|
-- 语言:Python 3.8+
|
|
|
-- CLI 框架:Click
|
|
|
-- 输出美化:Rich
|
|
|
-
|
|
|
-功能需求:
|
|
|
-1. 支持多种重命名规则:
|
|
|
- - 添加前缀/后缀
|
|
|
- - 替换文本
|
|
|
- - 序号命名
|
|
|
- - 日期命名
|
|
|
-2. 预览模式(不实际执行)
|
|
|
-3. 递归处理子目录(可选)
|
|
|
-4. 支持文件类型过滤
|
|
|
-
|
|
|
-请帮我:
|
|
|
-1. 确认技术栈是否合适
|
|
|
-2. 生成项目结构
|
|
|
-3. 一步步指导我完成开发
|
|
|
-
|
|
|
-要求:每完成一步问我是否成功,再继续下一步。
|
|
|
-```
|
|
|
-
|
|
|
-## 🏗️ 项目结构
|
|
|
-
|
|
|
-```
|
|
|
-file-renamer/
|
|
|
-├── renamer/
|
|
|
-│ ├── __init__.py
|
|
|
-│ ├── cli.py # CLI 入口
|
|
|
-│ ├── core.py # 核心逻辑
|
|
|
-│ └── rules.py # 重命名规则
|
|
|
-├── tests/
|
|
|
-│ └── test_core.py
|
|
|
-├── pyproject.toml
|
|
|
-└── README.md
|
|
|
-```
|
|
|
-
|
|
|
-## 🔧 核心代码
|
|
|
-
|
|
|
-### CLI 入口 (renamer/cli.py)
|
|
|
-
|
|
|
-```python
|
|
|
-import click
|
|
|
-from rich.console import Console
|
|
|
-from rich.table import Table
|
|
|
-from .core import FileRenamer
|
|
|
-from .rules import PrefixRule, SuffixRule, ReplaceRule, SequenceRule
|
|
|
-
|
|
|
-console = Console()
|
|
|
-
|
|
|
-@click.group()
|
|
|
-@click.version_option(version='1.0.0')
|
|
|
-def cli():
|
|
|
- """文件批量重命名工具"""
|
|
|
- pass
|
|
|
-
|
|
|
-@cli.command()
|
|
|
-@click.argument('directory', type=click.Path(exists=True))
|
|
|
-@click.option('--prefix', '-p', help='添加前缀')
|
|
|
-@click.option('--suffix', '-s', help='添加后缀')
|
|
|
-@click.option('--replace', '-r', nargs=2, help='替换文本 (旧文本 新文本)')
|
|
|
-@click.option('--sequence', '-n', is_flag=True, help='序号命名')
|
|
|
-@click.option('--ext', '-e', multiple=True, help='文件扩展名过滤')
|
|
|
-@click.option('--recursive', '-R', is_flag=True, help='递归处理子目录')
|
|
|
-@click.option('--dry-run', '-d', is_flag=True, help='预览模式')
|
|
|
-def rename(directory, prefix, suffix, replace, sequence, ext, recursive, dry_run):
|
|
|
- """批量重命名文件"""
|
|
|
- renamer = FileRenamer(directory, recursive=recursive, extensions=ext)
|
|
|
-
|
|
|
- # 添加规则
|
|
|
- if prefix:
|
|
|
- renamer.add_rule(PrefixRule(prefix))
|
|
|
- if suffix:
|
|
|
- renamer.add_rule(SuffixRule(suffix))
|
|
|
- if replace:
|
|
|
- renamer.add_rule(ReplaceRule(replace[0], replace[1]))
|
|
|
- if sequence:
|
|
|
- renamer.add_rule(SequenceRule())
|
|
|
-
|
|
|
- if not renamer.rules:
|
|
|
- console.print("[red]错误:请至少指定一个重命名规则[/red]")
|
|
|
- return
|
|
|
-
|
|
|
- # 获取预览
|
|
|
- changes = renamer.preview()
|
|
|
-
|
|
|
- if not changes:
|
|
|
- console.print("[yellow]没有找到匹配的文件[/yellow]")
|
|
|
- return
|
|
|
-
|
|
|
- # 显示预览表格
|
|
|
- table = Table(title="重命名预览")
|
|
|
- table.add_column("原文件名", style="cyan")
|
|
|
- table.add_column("新文件名", style="green")
|
|
|
-
|
|
|
- for old, new in changes:
|
|
|
- table.add_row(old, new)
|
|
|
-
|
|
|
- console.print(table)
|
|
|
- console.print(f"\n共 [bold]{len(changes)}[/bold] 个文件")
|
|
|
-
|
|
|
- if dry_run:
|
|
|
- console.print("[yellow]预览模式,未执行实际操作[/yellow]")
|
|
|
- return
|
|
|
-
|
|
|
- # 确认执行
|
|
|
- if click.confirm('确认执行重命名?'):
|
|
|
- renamer.execute()
|
|
|
- console.print("[green]✓ 重命名完成![/green]")
|
|
|
- else:
|
|
|
- console.print("[yellow]已取消[/yellow]")
|
|
|
-
|
|
|
-if __name__ == '__main__':
|
|
|
- cli()
|
|
|
-```
|
|
|
-
|
|
|
-### 核心逻辑 (renamer/core.py)
|
|
|
-
|
|
|
-```python
|
|
|
-import os
|
|
|
-from pathlib import Path
|
|
|
-from typing import List, Tuple
|
|
|
-
|
|
|
-class FileRenamer:
|
|
|
- def __init__(self, directory: str, recursive: bool = False, extensions: tuple = ()):
|
|
|
- self.directory = Path(directory)
|
|
|
- self.recursive = recursive
|
|
|
- self.extensions = extensions
|
|
|
- self.rules = []
|
|
|
-
|
|
|
- def add_rule(self, rule):
|
|
|
- self.rules.append(rule)
|
|
|
-
|
|
|
- def get_files(self) -> List[Path]:
|
|
|
- pattern = '**/*' if self.recursive else '*'
|
|
|
- files = []
|
|
|
- for path in self.directory.glob(pattern):
|
|
|
- if path.is_file():
|
|
|
- if self.extensions:
|
|
|
- if path.suffix.lower() in [f'.{e.lower()}' for e in self.extensions]:
|
|
|
- files.append(path)
|
|
|
- else:
|
|
|
- files.append(path)
|
|
|
- return sorted(files)
|
|
|
-
|
|
|
- def apply_rules(self, filename: str) -> str:
|
|
|
- result = filename
|
|
|
- for rule in self.rules:
|
|
|
- result = rule.apply(result)
|
|
|
- return result
|
|
|
-
|
|
|
- def preview(self) -> List[Tuple[str, str]]:
|
|
|
- changes = []
|
|
|
- for file in self.get_files():
|
|
|
- old_name = file.stem
|
|
|
- new_name = self.apply_rules(old_name)
|
|
|
- if old_name != new_name:
|
|
|
- changes.append((file.name, new_name + file.suffix))
|
|
|
- return changes
|
|
|
-
|
|
|
- def execute(self):
|
|
|
- for file in self.get_files():
|
|
|
- old_name = file.stem
|
|
|
- new_name = self.apply_rules(old_name)
|
|
|
- if old_name != new_name:
|
|
|
- new_path = file.parent / (new_name + file.suffix)
|
|
|
- file.rename(new_path)
|
|
|
-```
|
|
|
-
|
|
|
-### 重命名规则 (renamer/rules.py)
|
|
|
-
|
|
|
-```python
|
|
|
-from abc import ABC, abstractmethod
|
|
|
-
|
|
|
-class Rule(ABC):
|
|
|
- @abstractmethod
|
|
|
- def apply(self, filename: str) -> str:
|
|
|
- pass
|
|
|
-
|
|
|
-class PrefixRule(Rule):
|
|
|
- def __init__(self, prefix: str):
|
|
|
- self.prefix = prefix
|
|
|
-
|
|
|
- def apply(self, filename: str) -> str:
|
|
|
- return f"{self.prefix}{filename}"
|
|
|
-
|
|
|
-class SuffixRule(Rule):
|
|
|
- def __init__(self, suffix: str):
|
|
|
- self.suffix = suffix
|
|
|
-
|
|
|
- def apply(self, filename: str) -> str:
|
|
|
- return f"{filename}{self.suffix}"
|
|
|
-
|
|
|
-class ReplaceRule(Rule):
|
|
|
- def __init__(self, old: str, new: str):
|
|
|
- self.old = old
|
|
|
- self.new = new
|
|
|
-
|
|
|
- def apply(self, filename: str) -> str:
|
|
|
- return filename.replace(self.old, self.new)
|
|
|
-
|
|
|
-class SequenceRule(Rule):
|
|
|
- def __init__(self, start: int = 1, padding: int = 3):
|
|
|
- self.counter = start
|
|
|
- self.padding = padding
|
|
|
-
|
|
|
- def apply(self, filename: str) -> str:
|
|
|
- result = f"{str(self.counter).zfill(self.padding)}_{filename}"
|
|
|
- self.counter += 1
|
|
|
- return result
|
|
|
-```
|
|
|
-
|
|
|
-## 📦 安装配置 (pyproject.toml)
|
|
|
-
|
|
|
-```toml
|
|
|
-[build-system]
|
|
|
-requires = ["setuptools>=61.0"]
|
|
|
-build-backend = "setuptools.build_meta"
|
|
|
-
|
|
|
-[project]
|
|
|
-name = "file-renamer"
|
|
|
-version = "1.0.0"
|
|
|
-description = "文件批量重命名工具"
|
|
|
-requires-python = ">=3.8"
|
|
|
-dependencies = [
|
|
|
- "click>=8.0",
|
|
|
- "rich>=13.0",
|
|
|
-]
|
|
|
-
|
|
|
-[project.scripts]
|
|
|
-renamer = "renamer.cli:cli"
|
|
|
-```
|
|
|
-
|
|
|
-## 🚀 使用示例
|
|
|
-
|
|
|
-```bash
|
|
|
-# 安装
|
|
|
-pip install -e .
|
|
|
-
|
|
|
-# 添加前缀
|
|
|
-renamer rename ./photos --prefix "2024_"
|
|
|
-
|
|
|
-# 替换文本
|
|
|
-renamer rename ./docs --replace "old" "new"
|
|
|
-
|
|
|
-# 序号命名 + 过滤扩展名
|
|
|
-renamer rename ./images --sequence --ext jpg --ext png
|
|
|
-
|
|
|
-# 预览模式
|
|
|
-renamer rename ./files --prefix "backup_" --dry-run
|
|
|
-
|
|
|
-# 递归处理
|
|
|
-renamer rename ./project --suffix "_v2" --recursive
|
|
|
-```
|
|
|
-
|
|
|
-## ✅ 验收清单
|
|
|
-
|
|
|
-- [ ] 命令行帮助信息正确显示
|
|
|
-- [ ] 前缀/后缀功能正常
|
|
|
-- [ ] 替换功能正常
|
|
|
-- [ ] 序号命名功能正常
|
|
|
-- [ ] 预览模式不执行实际操作
|
|
|
-- [ ] 扩展名过滤正常
|
|
|
-- [ ] 递归处理正常
|
|
|
-
|
|
|
-## 💡 进阶挑战
|
|
|
-
|
|
|
-- 添加撤销功能(记录操作日志)
|
|
|
-- 支持正则表达式
|
|
|
-- 添加日期格式化规则
|
|
|
-- 支持配置文件
|
|
|
-- 添加交互式模式
|
|
|
+*(内容待填充...)*
|