OpenCode 实践课:4 分钟用 Python 写出一个命令行文件批量重命名工具

项目介绍

在日常开发中,我们经常需要对大量文件进行批量重命名——给所有 .txt 文件加前缀、把空格替换为下划线、按序号重命名照片。本文将展示如何用 OpenCode 在 4 分钟内从零开发一个功能完备的命令行文件批量重命名工具。

  • 技术栈:Python 3(纯标准库)
  • 代码量:约 70 行
  • 功能:前缀/后缀添加、正则替换、序号重命名、大小写转换、preview 预览模式

准备工作

安装 Python 3.8+

安装 OpenCode(参考 opencode.ai 文档)

打开终端,进入你的项目目录

实践过程

第一步:一句话描述需求

打开终端,启动 OpenCode,我直接描述了想要的功能:

> 用 Python 写一个命令行文件批量重命名工具,支持以下功能:
> 1. 添加前缀/后缀
> 2. 正则表达式替换
> 3. 序号重命名(如 file_001.txt, file_002.txt)
> 4. 大小写转换(upper/lower)
> 5. --dry-run 预览模式,不实际修改文件
> 6. 使用 argparse 处理命令行参数
> 代码控制在 100 行左右,不要用第三方库。

OpenCode 立刻开始分析项目目录结构,然后直接生成了完整的 argparse 骨架和核心重命名逻辑。

关键要点: 你不需要逐行指导 OpenCode。只要把功能列表说清楚,它就能一次性生成结构良好的完整代码。这比你自己查 argparse 文档再手动拼装快得多。

第二步:核心逻辑一步到位

OpenCode 生成的核心结构中包含了一个 sorted() 调用——它会自动给文件排序以保证序号一致性:

files = sorted(
    f for f in os.listdir(args.directory)
    if os.path.isfile(os.path.join(args.directory, f))
)

然后遍历文件,根据参数决定如何生成新文件名:

for i, filename in enumerate(files, start=args.index_start):
    name, ext = os.path.splitext(filename)
    new_name = name

    if args.case == 'upper':
        new_name = new_name.upper()
    elif args.case == 'lower':
        new_name = new_name.lower()

    if args.regex:
        new_name = re.sub(args.regex, args.replace, new_name)

关键要点: OpenCode 会自动处理边界情况,比如过滤掉目录只处理文件、用 os.path.splitext 正确分离扩展名。这些细节你不需要自己操心。

第三步:增量添加安全保护

看到基础代码后,我发现缺少文件名冲突的保护,于是接着对 OpenCode 说:

> 如果新文件名已存在,需要跳过并给出警告。dry-run 模式下用 [DRY RUN] 前缀输出。

OpenCode 在正确的位置插入了冲突检测:

target = os.path.join(args.directory, new_filename)
if os.path.exists(target):
    print(f'警告: 目标文件已存在,跳过 - {new_filename}', file=sys.stderr)
    continue

以及 dry-run 输出逻辑:

for src, dst in renamed:
    if args.dry_run:
        print(f'[DRY RUN] {os.path.basename(src)} -> {os.path.basename(dst)}')
    else:
        os.rename(src, dst)
        print(f'{os.path.basename(src)} -> {os.path.basename(dst)}')

关键要点: 增量修改就像和同事沟通一样自然。告诉 OpenCode 你要加什么,它会找到正确位置,不会破坏已有逻辑。

完整代码

import argparse
import os
import re
import sys


def main():
    parser = argparse.ArgumentParser(description='文件批量重命名工具')
    parser.add_argument('directory', help='目标目录')
    parser.add_argument('--prefix', default='', help='添加前缀')
    parser.add_argument('--suffix', default='', help='添加后缀(不含扩展名)')
    parser.add_argument('--regex', help='正则匹配模式')
    parser.add_argument('--replace', default='', help='替换字符串')
    parser.add_argument('--case', choices=['upper', 'lower'], help='大小写转换')
    parser.add_argument('--index', action='store_true', help='序号重命名')
    parser.add_argument('--index-start', type=int, default=1, help='起始序号')
    parser.add_argument('--index-pad', type=int, default=3, help='序号位数')
    parser.add_argument('--dry-run', action='store_true', help='预览模式')
    args = parser.parse_args()

    if not os.path.isdir(args.directory):
        print(f'错误: 目录不存在 - {args.directory}', file=sys.stderr)
        sys.exit(1)

    files = sorted(
        f for f in os.listdir(args.directory)
        if os.path.isfile(os.path.join(args.directory, f))
    )
    renamed = []

    for i, filename in enumerate(files, start=args.index_start):
        name, ext = os.path.splitext(filename)
        new_name = name

        if args.case == 'upper':
            new_name = new_name.upper()
        elif args.case == 'lower':
            new_name = new_name.lower()

        if args.regex:
            new_name = re.sub(args.regex, args.replace, new_name)

        if args.index:
            new_name = f'{args.prefix}{args.replace or new_name}_{i:0{args.index_pad}d}'
        else:
            new_name = f'{args.prefix}{new_name}{args.suffix}'

        new_filename = new_name + ext

        if new_filename == filename:
            continue

        target = os.path.join(args.directory, new_filename)
        if os.path.exists(target):
            print(f'警告: 目标文件已存在,跳过 - {new_filename}', file=sys.stderr)
            continue

        renamed.append((os.path.join(args.directory, filename), target))

    for src, dst in renamed:
        if args.dry_run:
            print(f'[DRY RUN] {os.path.basename(src)} -> {os.path.basename(dst)}')
        else:
            os.rename(src, dst)
            print(f'{os.path.basename(src)} -> {os.path.basename(dst)}')

    print(f'共处理 {len(renamed)} 个文件' + (' (预览模式)' if args.dry_run else ''))


if __name__ == '__main__':
    main()

使用示例

# 预览模式:添加前缀(不实际修改文件)
python rename.py ./test --prefix "backup_" --dry-run

# 序号重命名照片
python rename.py ./photos --index --index-start 1 --index-pad 3

# 正则替换:将文件名中的空格替换为下划线
python rename.py ./data --regex '\s+' --replace '_'

# 转为小写并添加后缀
python rename.py ./docs --case lower --suffix "_v2"

小结

用 OpenCode 开发这个小工具的体验:

速度惊人:从描述需求到可运行代码,全程不到 4 分钟

交互直观:用自然语言描述即可,无需记忆 argparse 的 action='store_true' 这类细节

迭代灵活:像对话一样逐步添加功能,每次修改都精准命中目标位置

质量可靠:生成的代码结构清晰、命名规范,边界情况(文件冲突、目录校验)都处理到位

相比传统开发需要查文档、写样板代码、调试参数解析,用 OpenCode 至少节省 80% 的时间。你只需要专注描述"做什么",把"怎么做"交给 AI。