ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

以SS号为准的批量重命名:Linux、Windows与Python脚本实战

以SS号为准的批量重命名:Linux、Windows与Python脚本实战 简介针对百度网盘秒传文件批量重命名的自动化需求这份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_filerename_$(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_张三.pdfSS 号固定占据第 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%的展开发生在解析阶段而不是循环逐次执行时。如果不开启enabledelayedexpansionfor 循环体内多个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 /TexFAT 盘也提示磁盘被 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(rD:\秒传文件) dst Path(rD:\按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(parentsTrue, exist_okTrue) 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(rD:\秒传文件) mapping {} with open(mapping.csv, encodingutf-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)encodingutf-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 时整个重命名链路才算走完。本文还有配套的精品资源点击获取
返回列表