You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python跨层级子文件夹增量备份脚本功能完善求助

问题描述

三次数据丢失后,我编写了Python脚本将电脑数据备份到外置SSD。当前脚本可正常处理源文件夹根目录的文件及文件夹,但无法检测深层子文件夹(如C:\Users\UNIX\Desktop\backup\x\w\a)内的新增或修改内容。不想因7KB的小文件改动复制100GB+的整个文件夹,附上现有代码,寻求实现仅复制子文件夹中新增或修改内容到对应目标路径的方法。

现有代码
import os, shutil
import os.path, time
import filecmp
from filecmp import dircmp

fob = open(r"C:\Users\UNIX\Desktop\backup\log.txt", "a")
dir_src = r"C:\Users\UNIX\Desktop\backup\x"
dir_dst = r"C:\Users\UNIX\Desktop\backup\y"

left_folder = []
right_folder =[]

def start_log():
    fob.write("\n")
    fob.write("===============")
    fob.write("Started at:      %s" % time.strftime("%c"))
    fob.write("\n")

def write_log():
    fob.write("File name:       %s" % os.path.basename(pathname))
    fob.write("     Last modified date:     %s" %time.ctime(os.path.getmtime(pathname)))
    fob.write("     Copied on:      %s" % time.strftime("%c"))
    fob.write("\n")

def print_diff_files(dcmp):
    global name, left_folder, right_folder, left_file, right_file
    for name in dcmp.diff_files:
        left_file = f"{dcmp.left}\{name}"
        right_file = f"{dcmp.right}\{name}"
        left_folder.append(left_file)
        right_folder.append(right_file)
    for sub_dcmp in dcmp.subdirs.values():
        print_diff_files(sub_dcmp)

start_log()

for w in os.listdir(dir_src):
    name = ""
    pathname = os.path.join(dir_src, w)
    try:
        spathname = os.path.join(dir_dst, w)
        if os.path.isdir(spathname):
            dcmp = dircmp(pathname, spathname)
            print_diff_files(dcmp)
            if name != "":
                for file in left_folder:
                    shutil.copy2(left_file, right_file)
                    write_log()
                    print("New folder version added: " + str(pathname))
                    print(f"Modified files: {right_file}")
        elif not filecmp.cmp(pathname, spathname):
            shutil.copy(pathname, dir_dst)
            write_log()
            print("New file version added: " + str(spathname))
    except:
        if os.path.isdir(pathname):
            shutil.copytree(pathname, spathname, dirs_exist_ok=True)
            write_log()
            print("New folder added: " + str(spathname))
        else:
            spathname = os.path.join(dir_dst, w)
            shutil.copy(pathname, dir_dst)
            write_log()
            print("New file added: " + str(spathname))
fob.close()

print("Done")
问题分析与修复方案

现有代码核心问题

  1. 变量覆盖:print_diff_files中left_file和right_file会被循环覆盖,最终仅复制最后一个差异文件
  2. 日志路径错误:write_log依赖全局pathname,无法正确传递子文件夹内的文件路径,导致日志记录错误
  3. 差异文件未复制:根目录遍历中的if name != ""条件永远不成立,收集到的差异文件未执行复制操作
  4. 未处理新增文件:仅检测修改过的文件(dcmp.diff_files),未处理源文件夹新增的文件/文件夹(dcmp.left_only)

修复后的代码

import os
import shutil
import time
import filecmp
from filecmp import dircmp

LOG_PATH = r"C:\Users\UNIX\Desktop\backup\log.txt"
DIR_SRC = r"C:\Users\UNIX\Desktop\backup\x"
DIR_DST = r"C:\Users\UNIX\Desktop\backup\y"

def start_log(file_obj):
    file_obj.write("\n")
    file_obj.write("===============\n")
    file_obj.write(f"Started at:      {time.strftime('%c')}\n")

def write_log(file_obj, file_path):
    file_name = os.path.basename(file_path)
    mtime = time.ctime(os.path.getmtime(file_path))
    copy_time = time.strftime("%c")
    file_obj.write(f"File name:       {file_name}\n")
    file_obj.write(f"     Last modified date:     {mtime}\n")
    file_obj.write(f"     Copied on:      {copy_time}\n")

def sync_dirs(dcmp, log_file):
    # 处理修改过的文件
    for file_name in dcmp.diff_files:
        src_file = os.path.join(dcmp.left, file_name)
        dst_file = os.path.join(dcmp.right, file_name)
        shutil.copy2(src_file, dst_file)
        write_log(log_file, src_file)
        print(f"Modified file copied: {dst_file}")
    
    # 处理源文件夹新增的文件/文件夹
    for file_name in dcmp.left_only:
        src_path = os.path.join(dcmp.left, file_name)
        dst_path = os.path.join(dcmp.right, file_name)
        if os.path.isdir(src_path):
            shutil.copytree(src_path, dst_path, dirs_exist_ok=True)
            write_log(log_file, src_path)
            print(f"New folder added: {dst_path}")
        else:
            shutil.copy2(src_path, dst_path)
            write_log(log_file, src_path)
            print(f"New file added: {dst_path}")
    
    # 递归处理子文件夹
    for sub_dcmp in dcmp.subdirs.values():
        sync_dirs(sub_dcmp, log_file)

def main():
    with open(LOG_PATH, "a") as log_file:
        start_log(log_file)
        
        for item in os.listdir(DIR_SRC):
            src_path = os.path.join(DIR_SRC, item)
            dst_path = os.path.join(DIR_DST, item)
            
            try:
                if os.path.isdir(src_path):
                    if os.path.exists(dst_path):
                        # 同步已存在文件夹的差异
                        dcmp = dircmp(src_path, dst_path)
                        sync_dirs(dcmp, log_file)
                    else:
                        # 复制新增的完整文件夹
                        shutil.copytree(src_path, dst_path)
                        write_log(log_file, src_path)
                        print(f"New folder added: {dst_path}")
                else:
                    if os.path.exists(dst_path):
                        # 检查已存在文件是否修改
                        if not filecmp.cmp(src_path, dst_path):
                            shutil.copy2(src_path, dst_path)
                            write_log(log_file, src_path)
                            print(f"Modified file copied: {dst_path}")
                    else:
                        # 复制新增文件
                        shutil.copy2(src_path, dst_path)
                        write_log(log_file, src_path)
                        print(f"New file added: {dst_path}")
            except Exception as e:
                print(f"Error processing {src_path}: {str(e)}")
        
        print("Done")

if __name__ == "__main__":
    main()

关键改动说明

  • 递归同步子文件夹:sync_dirs函数递归处理所有层级子文件夹的差异,直接在递归中完成复制和日志记录,避免全局变量问题
  • 覆盖全场景:同时处理修改文件(diff_files)和新增文件/文件夹(left_only)
  • 日志函数优化:传递当前文件路径作为参数,确保日志记录正确的文件信息
  • 代码结构优化:使用with语句管理日志文件,避免资源泄漏;拆分功能函数,提升可读性
  • 错误处理:新增异常捕获并打印错误信息,方便排查问题

内容的提问来源于stack exchange,提问作者Catalin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 08:35:15