使用Python shutil.move移动文件时丢失文件的技术咨询
我来帮你拆解问题,先逐个解答你的疑问,再分析你代码里藏着的坑,最后给你测试和修正方案:
1. shutil.move() 会把文件移去临时文件夹吗?
不会。shutil.move() 的设计就是直接把文件/目录从你指定的源路径转移到目标路径,不会默认用到临时文件夹。只有当源和目标在不同文件系统(比如从本地磁盘移到网络共享盘)时,它会先复制文件到目标,再删除源文件,但全程都是指向你设定的目标路径,不会绕去临时目录。
2. shutil.move() 会删除源文件吗?
这正是它的核心功能:
- 如果源和目标在同一个文件系统下,它会执行重命名操作(和系统的
mv命令一样),源文件会直接从原位置消失,出现在目标路径。 - 如果跨文件系统,它会先复制文件到目标,然后删除源文件。
- 注意:如果移动过程中出现权限不足、目标路径不可写这类错误,可能会留下半完成的文件,但只要复制/重命名成功,源文件一定会被移除。
3. 怎么测试脚本找出文件丢失的原因?
结合你的代码情况,推荐这几个测试步骤:
- 加日志打印:在移动文件前,打印源路径和目标路径,确认路径是否符合你的预期。比如加一行:
print(f"准备移动: {os.path.join(path, rawfiles)} -> {os.path.join(desination, rawfiles)}") - 先用
copy代替move测试:把shutil.move()换成shutil.copy(),这样即使出错,源文件还在,方便你排查路径是否正确。 - 单文件测试:先挑一个文件单独处理,注释掉批量循环的逻辑,验证单个文件能不能准确移到你想要的目录。
- 检查路径和权限:确认目标路径的拼写(比如你代码里变量名写的
desination是笔误,但路径字符串本身是对的?仔细核对dropbox + "/"+ samples + "/raw/"是不是你要的路径),同时确保脚本对源目录和目标目录有读写权限。
你的代码里的关键问题
仔细看你的filemover函数,发现一个严重的循环冗余问题:
for samples in ID: for fs in files: if samples in fs: # 创建目标目录 for rawfiles in fnmatch.filter(files, pat="*"): # 这里遍历所有文件 if samples in rawfiles: shutil.move(...)
当某个samples匹配到第一个fs时,内层的fnmatch循环会把所有匹配该samples的文件全部移走。但外层的for fs in files还会继续跑,当后续的fs也匹配这个samples时,再次进入内层循环,此时源文件已经被移走了,就会抛出“文件不存在”的错误。但其实文件已经成功移动到目标目录了,只是后续重复循环导致报错。
另外,fnmatch.filter(files, pat="*")完全没必要,因为files已经是你筛选好的以"SSE"开头的文件列表,直接遍历files就行。
修正后的代码建议
import os import shutil import pandas as pd path = '/path/to/source' dropbox = '/path/to/dropbox' # 获取所有以SSE开头的文件名(不带路径) files = [f for f in os.listdir(path) if f.startswith("SSE")] # 提取唯一的样本ID sam_lis = [sam.split('_')[5] for sam in files] sam_lis = pd.unique(sam_lis).tolist() def filemover(sample_ids, file_list, target_root): """将文件移动到按样本ID创建的目标目录""" for sample_id in sample_ids: # 用os.path.join拼接路径,避免手动拼字符串的分隔符问题 dest_dir = os.path.join(target_root, sample_id, 'raw') # 自动创建目录(包括父目录),无需手动判断是否存在 os.makedirs(dest_dir, exist_ok=True) # 遍历文件列表,移动匹配当前样本ID的文件 for filename in file_list: if sample_id in filename: src_path = os.path.join(path, filename) dest_path = os.path.join(dest_dir, filename) # 打印日志跟踪移动过程 print(f"移动文件: {src_path} -> {dest_path}") # 先检查源文件是否存在,避免重复移动报错 if os.path.exists(src_path): shutil.move(src_path, dest_path) else: print(f"警告:源文件已不存在,跳过: {src_path}") # 调用函数执行移动 filemover(sam_lis, files, dropbox)
修正点说明:
- 用
os.path.join()拼接路径,避免了手动写/导致的跨系统兼容问题(比如Windows用\)。 - 用
os.makedirs(dest_dir, exist_ok=True)替代手动判断目录是否存在,代码更简洁,还能自动创建父目录。 - 移除了冗余的嵌套循环,每个样本ID只遍历一次文件列表,避免重复移动文件。
- 添加了源文件存在性检查,防止重复移动导致的“文件不存在”错误。
- 增加了日志打印,方便你跟踪每个文件的移动情况,出问题时能快速定位。
内容的提问来源于stack exchange,提问作者ARJ




