简介:针对百度网盘秒传文件批量重命名的自动化需求,这份Python工具包以“SS号”为线索,自动查询对应书名并完成文件重命名,适合需要处理大量云盘文件的开发、运维人员参考使用。压缩包体积约3KB,共含2个文件:一个Python主脚本,实现HTTP请求、JSON解析、os重命名等核心逻辑;一个txt说明文件,用于存放待重命名文件列表及使用提示。脚本采用requests库调用接口,配合正则提取文件名中的SS号,清晰展示了从API请求到本地文件操作的完整流程。目前已有135人学习,可帮助读者快速掌握第三方接口调用与自动化文件管理的基本套路,稍加修改即可适配不同的查询接口和命名规则,节省重复劳动。
1. 秒传文件下载下来命名一塌糊涂,SS号才是重命名的可靠锚点
从别人手里接手一批秒传分享的文件,解压后大概率是tmp_8f2k3_001.pdf、2024_0312_副本.pdf这类名字,原文件名和归属信息都被打散了。秒传机制本身只负责“快速定位文件”,并不承诺保留你肉眼可读的命名结构,所以归档前必须重新梳理命名。操作里最难的不是批量改,而是“新名字从哪里来”:文件名里往往只剩学号、工单号这类编号是稳定的,也就是这里的 SS 号。本文就按这个场景展开:先定提取规则,再分别用 Linux shell、Windows bat 和 Python 三种方式批量重命名,最后讲讲怎么验证没把文件改错。适合手里攒了几百个乱名文件、需要对应到人员或工单编号的运维、教研和运营同学。
2. 提取SS号规则先行,Linux shell批量重命名脚本的写法
2.1 先从文件名里认出SS号的三种常见格式
拿到素材的第一步不是写脚本,而是先看几十个文件名,把规律列出来。我实际处理过的秒传文件命名基本逃不开这三种形态:第一种是“前缀分隔符 + SS号 + 姓名/题目”,比如2024_SS10020345_张三课题.pdf;第二种是“纯数字连续一段”,例如10020345_结题报告.pdf,需要自己判断哪一段是编号;第三种最麻烦,文件名是一串 hash 加随机字符,例如a3f9c1e2d8b74f0a9123_10020345.pdf,编号藏在尾巴上,还要防止正则误抓到 hash 里的数字片段。
| 命名形态 | 示例 | 提取思路 |
|---|---|---|
| 显式带SS前缀 | 2024_SS10020345_张三.pdf | 匹配SS后接 6~10 位数字 |
| 纯数字编号 | 10020345_课题申报书.pdf | 锚定文件名头部,取前 15 个字符内的连续数字 |
| hash加编号 | a3f9c1e2d8b74f0a9123_10020345.pdf | 取最后一个下划线后的内容,再抠数字 |
判断标准我一般用一个笨但有效的办法:把候选数字串拿到分享者的花名册或者工单表里 grep 一遍,能精确匹配上的那个才是 SS 号。写正则之前先做这一步,比在脚本里反复调 pattern 高效得多。
2.2 用sed和grep把SS号从文件名中抠出来
Linux 下最顺手的做法是grep -oE配合一个收紧的正则。需要注意[0-9]{6,10}在长 hash 文件名里非常容易误命中,所以我建议先对文件名做截断,只在前 15 到 20 个字符里找编号,这段区域内出现连续数字的概率高且可靠。下面这个表达式会在文件名头部找“可选大写前缀 + 6 到 10 位数字”,输出时只留下数字本身。
echo "2024_SS10020345_张三.pdf" | head -c 20 | grep -oE '(SS|NO|学号)?[0-9]{6,10}' | head -n 1 | tr -d '[:alpha:]'这行命令的解析顺序是:先用head -c 20把字符串截前 20 个字节,避免尾部 hash 干扰;grep -oE只输出匹配片段;head -n 1保证同一条记录里若出现两个候选数字只取第一个;最后的tr -d '[:alpha:]'把SS、NO这类前缀去掉,只留纯编号。这里有几个参数需要按实际数据调整:一是截断长度,如果 SS 号本身很长,建议从 20 提到 30;二是最小位数,团队内若是 8 位工号,就把{6,10}收紧成{8,8},误报立刻少一半;三是如果花名册里存在“SS号以 0 开头”的情况,注意 grep 匹配后变量里前导零是否被保留,shell 变量赋值不会丢零,但后续加减运算会,重命名场景只做字符串拼接,没有这个问题。
2.3 完整的for循环mv脚本,失败文件单独记日志
规则确认后,直接上完整的批量重命名脚本。这个脚本我会写成接受一个目录参数的形式,方便在多个目录间复用,而不是把路径写死。
#!/usr/bin/env bash set -uo pipefail src_dir="${1:-/data/秒传文件}" log_file="rename_$(date +%Y%m%d_%H%M%S).log" for f in "$src_dir"/*; do [ -f "$f" ] || continue # 跳过子目录和空文件 bname="$(basename "$f")" head="${bname:0:20}" # 只看前20个字符,防hash干扰 ss="$(echo "$head" | grep -oE '(SS|NO|学号)?[0-9]{6,10}' | head -n 1 | tr -d '[:alpha:]')" if [ -z "$ss" ]; then echo "SKIP no_ss: $bname" >> "$log_file" # 没有编号的单独一行 continue fi # 文件名已经以 SS号_ 开头则跳过,保证脚本重复执行不会叠加前缀 case "$bname" in "${ss}_"*) echo "SKIP already: $bname" >> "$log_file"; continue ;; esac dirn="$(dirname "$f")" newf="$dirn/${ss}_${bname}" printf '%s -> %s\n' "$bname" "$(basename "$newf")" >> "$log_file" # mv "$f" "$newf" # 确认日志无误后,取消这一行的注释执行真移动 done脚本先做三个前置检查:第一,[ -f "$f" ]保证不把目录当文件处理;第二,文件名头部 20 字符里找不到可靠编号的直接进日志跳过,而不是猜一个数字硬改名;第三,判断文件名是否已经带SS号_前缀,避免重复执行时出现10020345_10020345_张三.pdf这种叠加错误。printf把“旧名 -> 新名”的对应关系记录到带时间戳的日志文件里,这条日志同时就是后续验证的依据。
需要调整的参数主要是head -c的截断长度和grep -oE里的位数范围。字符截取用${bname:0:20}在 bash 里按字符数切,中文文件名不会切出半个字的乱码,这一点比用cut -c更稳。日志和实际改名动作分离是这套脚本的关键,第一次跑永远只输出不执行,人眼确认过对应关系再打开 mv。
2.4 比直接用rename命令更稳的两个习惯
Linux 自带的rename命令能一行实现替换,但它的默认表达式语法在 util-linux 和 perl 版本之间不统一,而且 sed 表达式一变就会批量误伤。我习惯用上面的 for 循环加 mv,主要原因有两个:一是每次改动都有日志,现场排错有依据;二是可以精确控制“哪些文件跳过”,rename 的表达式过滤做不到这么细。如果你确实想用 rename,建议先跑rename -n做 dry-run,-n只打印不执行,确认输出列表完全正确再去掉-n运行。文件名里包含中文和空格时,记得整个路径用双引号包住,否则 shell 分词会把路径拆碎。
3. 在Windows上写bat批量重命名,先解决编码和权限报错
3.1 for /f加ren的最小脚本结构
Windows 下有图形界面右键重命名,但几百个文件还是得靠脚本。bat 没有正则能力,所以我一般只在“SS 号位置固定”的前提下用它,比如文件名统一是2024_10020345_张三.pdf,SS 号固定占据第 6 到第 13 位,这时用变量截取就能稳定提取,不必上 PowerShell。Windows 批处理的最小可行脚本长这样:
@echo off setlocal enabledelayedexpansion cd /d "D:\秒传文件" if not exist "renamed" mkdir "renamed" for /f "delims=" %%i in ('dir /b *.pdf') do ( set "name=%%~ni" set "ss=!name:~5,8!" if "!ss!"=="" ( echo skip: %%i ) else ( echo !ss!_%%i rem 确认输出无误后,把下面这行的rem去掉 rem ren "%%i" "!ss!_%%i" ) ) endlocalfor /f "delims="的作用是逐行读取dir /b *.pdf的输出,delims=表示不按空格或制表符分割整行文件名,避免名字里的空格把行切断。%%~ni取出不带扩展名的文件名,!name:~5,8!表示从第 6 个字符开始取 8 个字符,这个偏移量要按你自己的文件命名格式改。脚本里先只 echo 出新名字,人工核对一批没问题后再把ren那行的rem去掉。setlocal enabledelayedexpansion必须保留,原因下一节说。
3.2 延迟扩展为什么不能省
bat 里%name%的展开发生在解析阶段,而不是循环逐次执行时。如果不开启enabledelayedexpansion,for 循环体内多个set引用同一个变量时会拿到循环开始前的旧值,结果新文件名永远是空或者错位。改用!name!和!ss!这种延迟展开语法,才能保证每次循环读取最新赋的值。如果你在 cmd 窗口里一行行执行没问题、一旦写成批处理就对不上号,问题基本都出在这个地方。
另外注意 bat 对中文的支持很别扭:批处理文件本身保存为 UTF-8 会导致中文文件名乱码,而chcp 65001只影响当前控制台代码页,无助于 bat 文件内已写死的中文文本。最省事的方案是把文件保存为 ANSI 编码,记事本里另存为时选择“ANSI”即可。如果脚本要长期维护,干脆 echo 提示信息用英文,文件名本身不写死中文,交给%%i运行时变量传递,能避开大半编码问题。
3.3 移动硬盘目录提示需要administrator权限的排查顺序
“移动硬盘新建了个文件夹,但是无法重命名跟删除,提示需 administrator 权限”这个报错在秒传文件解压目录里很常见,不是脚本问题,而是文件系统权限和属性问题。按以下顺序排查,不要一开始就右键“以管理员身份运行”,那个不一定有效。
| 现象 | 原因 | 处理命令 |
|---|---|---|
| 重命名提示“拒绝访问” | 文件带只读或隐藏属性 | attrib -R -S -H "D:\目录\*" /S |
| 提示需要管理员权限且点了没用 | 所有者不是当前用户 | takeown /f "D:\目录" /r /d y |
| 图标带锁,重命名权限不足 | NTFS ACL 拒绝当前用户 | icacls "D:\目录" /grant "%USERNAME%":F /T |
| exFAT 盘也提示 | 磁盘被 BitLocker 锁或卷只读 | 检查 BitLocker 状态和写保护开关 |
顺序是有讲究的:先清只读属性,再做 takeown 拿所有权,最后用 icacls 显式授权。三步都做完仍然报错,就把目标文件夹整个复制到本地 NTFS 分区再操作,移动硬盘上的元数据损坏不是靠命令能修复的。批处理脚本本身若涉及写系统目录,才需要“以管理员身份运行”,处理普通移动硬盘数据不该碰管理员权限。
4. 嵌套目录和重名冲突,用Python做兜底归档
4.1 递归遍历目录,按SS号自动归档
秒传链接下载的资源经常还带着多层目录结构,shell 的dir /b和for f in dir/*都只是单层遍历,此时我直接用 Python 做兜底,pathlib的rglob能一次递归所有子目录。下面的脚本扫描源目录下所有 PDF 和 Word 文件,从文件名中提取 SS 号,并把文件移动到以 SS 号命名的子目录里,实现按编号归档。
import re import shutil from pathlib import Path src = Path(r"D:\秒传文件") dst = Path(r"D:\按SS归档") ss_pat = re.compile(r"(?:SS|NO|学号)?(\d{6,10})") for p in src.rglob("*"): if not p.is_file() or p.suffix.lower() not in (".pdf", ".docx", ".doc"): continue head = p.name[:20] # 同样只取前20字符,减少hash干扰 m = ss_pat.search(head) if not m: print("SKIP", p) continue ss = m.group(1) target_dir = dst / ss target_dir.mkdir(parents=True, exist_ok=True) target = target_dir / (ss + "_" + p.name) if target.exists(): # 同名冲突直接报告,不覆盖 print("CONFLICT", target) continue print(p, "->", target) shutil.move(str(p), target) # 确认列表无误后再启用这行代码的逻辑顺序是:排除非文件和非目标扩展名;文件名截前 20 字符后过正则;找到 SS 号就建立目标目录,找不到就打印 SKIP 不猜名。target.exists()是保护开关,如果目标已经存在说明重复编号,程序直接跳过而不会用后到的文件覆盖先到的文件。shutil.move是跨目录重命名,相当于mv。第一次运行时建议注释掉shutil.move那行,只保留print,观察输出列表。
4.2 用CSV建立“旧文件名→SS号”映射
对于文件名里确实提取不出编号的情况,就得外部提供映射关系。比如分享者给了一个花名册表,里面有原始文件名对应的 SS 号和姓名,这种场景下正则基本没戏,直接读 CSV 是唯一靠谱路径。假设 CSV 长这样:
old_filename,ss tmp_xk2f_001.pdf,20240001 tmp_xk2f_002.pdf,20240002 tmp_xk2f_003.pdf,20240003对应代码片段:
import csv from pathlib import Path import shutil src = Path(r"D:\秒传文件") mapping = {} with open("mapping.csv", encoding="utf-8-sig") as fh: # 兼容Excel带BOM for row in csv.DictReader(fh): mapping[row["old_filename"]] = row["ss"] for old_name, ss in mapping.items(): p = src / old_name if not p.exists(): print("MISSING", old_name) continue new_name = f"{ss}_{old_name}" p.rename(p.with_name(new_name)) print(old_name, "->", new_name)encoding="utf-8-sig"这个参数是为 Excel 导出的 CSV 准备的,带 BOM 头时普通utf-8会把第一列名解析成带\ufeff的脏字符串,导致映射表 key 对不上文件名。值得单独提一句的是,这里用p.rename而不是shutil.move,因为新旧路径在同一个目录下,Path.rename足够且不会误判跨盘移动。
4.3 dry-run先看再写,避免覆盖同名文件
Python 版本的优势在灵活,代价是误操作面也大。所以无论正则方案还是 CSV 方案,run 之前都必须干跑一遍。所谓干跑是把写操作全部换成打印:
# 正式改动之前,先执行这一段 for p in src.rglob("*"): if not p.is_file(): continue ss = ss_pat.search(p.name[:20]) if not ss: continue target = dst / ss.group(1) / (ss.group(1) + "_" + p.name) print("MOVE", p, "->", target)输出的每一行都是一条将来会真实发生的 mv 动作,我用sort排序后与花名册比对数量。源文件总数减去 SKIP 数等于待改名数,待改名数减去 CONFLICT 数等于日志里 MOVE 的行数,这三个数能对上,才真正执行写操作。这个环节省掉,一旦正则写宽了,几百个文件被改到错误编号,再找回的成本远比多跑一遍脚本高。
5. 重命名后的三遍核对,确认内容和对应都没错
5.1 重命名前后目录做一次diff
批量改完名先验证名称层面的正确性。方法是在执行前用第 2 章的日志文件保存了每一对“旧名新名”,执行后重新扫描目录。
find /data/秒传文件 -type f | sort > before.txt # 执行批量重命名脚本 find /data/秒传文件 -type f | sort > after.txt comm -3 before.txt after.txt | head -n 20comm -3输出两边不同的行,diff 到的是部分改名动作;再对照日志里的printf记录,如果每行都对应上了,说明没有文件凭空消失。这步查的是“文件级别的增删”,确认移动过程没有因为 mv 失败丢文件。
5.2 用哈希校验文件载体没被改过
重命名通常不会改动文件内容,但大批量移动时盘符中断、复制粘贴误操作都可能造成文件损坏。稳妥做法是在移动前对每个文件算一次 SHA-1,移动后再算一次。文件数量大时不需要全部校验,随机抽查 10% 即可,但抽查必须保证同一条文件的两次哈希是同一个,而不是只看总数一致。可以用下面这个片段:
for f in /data/秒传文件/*; do [ -f "$f" ] || continue old_hash=$(sha1sum "$f" | cut -d' ' -f1) new="/data/按SS归档/$(basename "$f")" [ -f "$new" ] || { echo "MISSING: $f"; continue; } new_hash=$(sha1sum "$new" | cut -d' ' -f1) [ "$old_hash" = "$new_hash" ] && echo "OK $f" || echo "BAD $f" done哈希比对在这里的定位很明确:它验证的是 mv/ren 动作是否真的“只改名不动内容”,排除移动过程中文件被截断或覆盖的风险。sha1sum换成md5sum也可以,但字数多时 SHA-1 比 MD5 冲突概率低,场景中选前者。
5.3 检查日志的最后一环
日志里的 SKIP 行是最容易被忽略的,它们表示无法提取 SS 号的文件,需要人工介入。批量任务完成的标志不是“mv 都跑完了”,而是 SKIP 行数是零,或者 SKIP 列表经过人工核对确认确实不需要改名。我通常会再执行一条命令检查残留乱名文件:
find /data/秒传文件 -type f ! -name "[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]_*" | wc -l这条命令统计目录里所有不是“8 位数字开头加下划线”的文件数量,结果加上日志中的 SKIP 数应该等于最初无法识别的文件数。当这条命令返回 0 时,整个重命名链路才算走完。
本文还有配套的精品资源,点击获取