使用os.walk遍历外部驱动器时遇冻结错误,如何解决?
解决os.walk遍历外部驱动器冻结问题并实现路径规范化
问题背景
需要对外部驱动器中的文件路径进行规范化(去除重音),使用os.walk()时脚本出现冻结,强制终止后抛出如下错误:
^CTraceback (most recent call last): File "~/normalize_filepaths.py", line 2 for root, dirs, files in os.walk(target, topdown = False): File "<frozen os>", line 377, in walk KeyboardInterrupt
相关代码片段:
import os import unicodedata import shutil def normalize(fp): """ >>> normalize("/Volumes/MM_BUP/MIGUEL/Acólitos") '/Volumes/MM_BUP/MIGUEL/Acolitos' >>> normalize("/Volumes/MM_BUP/MIGUEL/Acólitos") '/Volumes/MM_BUP/MIGUEL/Acolitos' >>> normalize("'This is my cup.' _ ゼロコ ZEROKO _ 紅茶の遊び方 _ mime _ clowning-8MgRJAXn1tE.mp4") "'This is my cup.' _ ZEROKO _ _ mime _ clowning-8MgRJAXn1tE.mp4" >>> normalize(" großer Tag") ' grosser Tag' """ fp = fp.replace("ß", "ss") name_clean = unicodedata.normalize('NFD', fp) return name_clean.encode('ascii', 'ignore').decode("ascii") def main(target="/some/path"): for root, dirs, files in os.walk(target, topdown = False): for name in files + dirs: filepath = os.path.join(root, name) clean = normalize(name) new_filepath = os.path.join(root, clean) shutil.move(filepath, new_filepath)
解决方案及优化点
1. 切换topdown=True遍历模式
topdown=False会先处理子目录再处理父目录,重命名父目录后会导致已遍历的子目录路径失效,引发遍历混乱甚至卡住。改为topdown=True后,可动态修改dirs列表,确保遍历路径始终正确。
2. 拆分文件与目录处理逻辑
单独处理文件和目录,避免混合处理导致的路径冲突;对目录重命名后更新dirs列表,让后续遍历使用新的目录名。
3. 增加错误处理与跳过逻辑
外部驱动器可能存在文件锁定、权限不足或系统特殊文件,添加try-except捕获异常,记录错误后继续执行,防止脚本因单个文件/目录出错而卡住。
4. 避免重复操作
先判断原路径和新路径是否一致,一致则跳过移动操作,减少不必要的IO开销。
5. 添加进度输出
打印当前处理的文件/目录,直观了解脚本运行状态,避免误以为脚本冻结。
修改后的完整代码
import os import unicodedata import shutil def normalize(fp): """ >>> normalize("/Volumes/MM_BUP/MIGUEL/Acólitos") '/Volumes/MM_BUP/MIGUEL/Acolitos' >>> normalize("/Volumes/MM_BUP/MIGUEL/Acólitos") '/Volumes/MM_BUP/MIGUEL/Acolitos' >>> normalize("'This is my cup.' _ ゼロコ ZEROKO _ 紅茶の遊び方 _ mime _ clowning-8MgRJAXn1tE.mp4") "'This is my cup.' _ ZEROKO _ _ mime _ clowning-8MgRJAXn1tE.mp4" >>> normalize(" großer Tag") ' grosser Tag' """ fp = fp.replace("ß", "ss") name_clean = unicodedata.normalize('NFD', fp) return name_clean.encode('ascii', 'ignore').decode("ascii") def process_item(root, name): original_path = os.path.join(root, name) clean_name = normalize(name) new_path = os.path.join(root, clean_name) if original_path == new_path: return try: shutil.move(original_path, new_path) print(f"Processed: {original_path} -> {new_path}") except Exception as e: print(f"Error processing {original_path}: {str(e)}") def main(target="/some/path"): for root, dirs, files in os.walk(target, topdown=True): # 处理文件 for name in files: process_item(root, name) # 处理目录并更新dirs updated_dirs = [] for name in dirs: original_path = os.path.join(root, name) clean_name = normalize(name) new_path = os.path.join(root, clean_name) if original_path != new_path: try: shutil.move(original_path, new_path) print(f"Renamed directory: {original_path} -> {new_path}") updated_dirs.append(clean_name) except Exception as e: print(f"Error renaming directory {original_path}: {str(e)}") updated_dirs.append(name) else: updated_dirs.append(name) dirs[:] = updated_dirs if __name__ == "__main__": # 替换为你的外部驱动器路径 main(target="/Volumes/MM_BUP")
内容的提问来源于stack exchange,提问作者ginjaemocoes
相关产品推荐
相关产品推荐

