关于使用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:]))

