您当前的位置:首页 > 计算机 > 编程开发 > Python

关于使用python脚本结合excel批量重命名文件夹

时间:12-14来源:作者:点击数:

关于使用python脚本结合excel批量重命名文件夹

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
批量重命名文件夹(基于 Excel 映射)
说明:读取 Excel 文件中两列:原文件夹名(old_col)和新文件夹名(new_col),在指定的基目录下依次按行将匹配的文件夹重命名。
用法示例(PowerShell):
  python 批量重命名文件夹.py -i mapping.xlsx -s 原名 -t 新名 -b C:\path\to\folders --dry-run
选项:
-i, --input         Excel 文件路径(支持 .xlsx/.xls),若文件有多个 sheet 可用 -S 指定 sheet 名
-s, --source-col    Excel 中表示原文件夹名的列名(必填)
-t, --target-col    Excel 中表示新文件夹名的列名(必填)
-b, --base-dir      要重命名的文件夹所在的基目录(默认:当前目录)
--dry-run           仅打印将要进行的重命名,不实际修改文件系统
--skip-missing      当原文件夹不存在时跳过(默认跳过)
--conflict-mode     冲突处理: skip(跳过), overwrite(覆盖), unique(添加序号) 默认 skip
依赖:pandas
"""
from __future__ import annotations
import argparse
import os
import sys
from typing import List, Tuple
try:
    import pandas as pd
except Exception:
    print('缺少依赖 pandas,请先运行: pip install pandas')
    raise
def read_mapping(input_file: str, sheet_name: str | None, src_col: str, tgt_col: str) -> List[Tuple[str, str, int]]:
    """读取映射表,返回列表 (原名, 新名, 行号)。"""
    kwargs = {}
    if sheet_name:
        kwargs['sheet_name'] = sheet_name
    try:
        df = pd.read_excel(input_file, **kwargs)
    except Exception as e:
        raise RuntimeError(f'读取 Excel 失败: {e}')
    if src_col not in df.columns or tgt_col not in df.columns:
        raise RuntimeError(
            f'缺少指定列: {src_col} 或 {tgt_col} 不在表头中。表头列: {list(df.columns)}')
    items = []
    for idx, row in df.iterrows():
        src = str(row[src_col]) if not pd.isna(row[src_col]) else ''
        tgt = str(row[tgt_col]) if not pd.isna(row[tgt_col]) else ''
        # +2 -> excel 行号(header + 1-based index)
        items.append((src, tgt, int(idx) + 2))
    return items
def safe_rename(src_path: str, tgt_path: str, conflict_mode: str) -> Tuple[bool, str]:
    """尝试重命名,返回 (成功, message)。conflict_mode: skip|overwrite|unique"""
    if not os.path.exists(src_path):
        return False, '源不存在'
    if os.path.exists(tgt_path):
        if os.path.samefile(src_path, tgt_path):
            return False, '源目标相同'
        if conflict_mode == 'skip':
            return False, '目标已存在(skip)'
        elif conflict_mode == 'overwrite':
            # 删除目标(谨慎)
            try:
                if os.path.isdir(tgt_path):
                    import shutil
                    shutil.rmtree(tgt_path)
                else:
                    os.remove(tgt_path)
            except Exception as e:
                return False, f'删除目标失败: {e}'
        elif conflict_mode == 'unique':
            # 给目标添加序号,直到可用
            base = tgt_path
            i = 1
            while os.path.exists(tgt_path):
                tgt_path = f"{base} ({i})"
                i += 1
    try:
        os.rename(src_path, tgt_path)
        return True, '重命名成功'
    except Exception as e:
        return False, f'重命名出错: {e}'
def main(argv: List[str]) -> int:
    parser = argparse.ArgumentParser(description='根据 Excel 的两列将文件夹批量重命名')
    parser.add_argument('-i', '--input', required=True, help='映射 Excel 文件路径')
    parser.add_argument('-S', '--sheet', default=None, help='sheet 名称或索引(可选)')
    parser.add_argument('-s', '--source-col', required=True, help='表示原文件夹名的列名')
    parser.add_argument('-t', '--target-col', required=True, help='表示新文件夹名的列名')
    parser.add_argument('-b', '--base-dir', default='.',
                        help='要在其中进行重命名的基目录(默认当前目录)')
    parser.add_argument('--dry-run', action='store_true', help='仅打印不执行')
    parser.add_argument('--skip-missing', action='store_true',
                        help='当源文件夹不存在时跳过(默认跳过)')
    parser.add_argument(
        '--conflict-mode', choices=['skip', 'overwrite', 'unique'], default='skip', help='目标已存在时的处理策略')
    args = parser.parse_args(argv)
    try:
        mappings = read_mapping(args.input, args.sheet,
                                args.source_col, args.target_col)
    except Exception as e:
        print('读取映射失败:', e)
        return 2
    base = os.path.abspath(args.base_dir)
    print('基目录:', base)
    successes = 0
    failures = 0
    for src, tgt, rowno in mappings:
        if not src:
            print(f'行 {rowno}: 源名为空,跳过')
            failures += 1
            continue
        src_path = os.path.join(base, src)
        tgt_path = os.path.join(base, tgt) if tgt else ''
        if not os.path.exists(src_path):
            print(f'行 {rowno}: 源 {src_path} 不存在,跳过')
            failures += 1
            continue
        if not tgt:
            print(f'行 {rowno}: 目标名为空,跳过')
            failures += 1
            continue
        if args.dry_run:
            print(f'[DRY] 行 {rowno}: "{src}" -> "{tgt}"')
            successes += 1
            continue
        ok, msg = safe_rename(src_path, tgt_path, args.conflict_mode)
        if ok:
            print(f'行 {rowno}: 重命名 "{src}" -> "{tgt}" 成功')
            successes += 1
        else:
            print(f'行 {rowno}: 重命名 "{src}" -> "{tgt}" 失败: {msg}')
            failures += 1
    print(f'完成: 成功 {successes}, 失败 {failures}')
    return 0
if __name__ == '__main__':
    sys.exit(main(sys.argv[1:]))

 

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐