python将指定目录下指定年限以前的所有文件转移到指定的目录

常用各类软硬件使用技巧、疑难、最新资讯等相关交流
回复
rungod
帖子: 63
注册时间: 2010-06-19 3:51

根据实际情况优化后:python将指定目录下指定年限以前的所有文件转移到指定的目录

帖子 rungod »

代码: 全选

import os
import shutil
import time
import logging
import sys
import io

# 强制设置控制台输出编码,防止控制台打印畸形字符时崩溃
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')

# 【核心续传机制】:在脚本同级目录下生成进度记录文件
PROGRESS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "move_progress.log")

def load_progress():
    """启动时加载已处理的文件夹清单"""
    if os.path.exists(PROGRESS_FILE):
        try:
            with open(PROGRESS_FILE, 'r', encoding='utf-8', errors='replace') as f:
                return set(line.strip() for line in f if line.strip())
        except Exception as e:
            logging.error(f"❌ 加载进度文件失败,将重新开始: {e}")
            return set()
    return set()

def save_progress(processed_dirs):
    """
    终极安全保存:原子写入 + 强制替换畸形字符,彻底解决 Windows 下的 surrogates 崩溃
    """
    temp_file = PROGRESS_FILE + '.tmp'
    try:
        # 1. 先写入临时文件
        with open(temp_file, 'w', encoding='utf-8') as f:
            for d in processed_dirs:
                # 【核心修复】:在写入前,强制将任何无法编码的孤立代理字符替换为 '?'
                # 这不会影响实际的文件移动,但能保证进度文件绝对不报错
                safe_d = d.encode('utf-8', errors='replace').decode('utf-8')
                f.write(safe_d + '\n')
            f.flush()
            os.fsync(f.fileno())  # 强制将数据写入磁盘
        
        # 2. 写入成功后,用临时文件原子性地替换原文件
        shutil.move(temp_file, PROGRESS_FILE)
    except Exception as e:
        logging.error(f"❌ 保存进度时发生错误: {e}")
        if os.path.exists(temp_file):
            try:
                os.remove(temp_file)
            except:
                pass

def move_old_files_safely(source_dir, target_dir, years=2):
    """
    终极断点续传版:彻底修复 Windows 网络路径下的畸形文件名和断网问题
    """
    current_time = time.time()
    threshold_time = current_time - (years * 365.25 * 24 * 60 * 60)
    
    moved_count = 0
    skipped_count = 0
    folder_skipped_count = 0
    error_count = 0

    # 1. 加载上次的进度
    processed_dirs = load_progress()
    if processed_dirs:
        logging.info(f"📂 检测到上次进度,已加载 {len(processed_dirs)} 个已处理目录,将自动跳过...")

    if not os.path.exists(source_dir):
        logging.error(f"❌ 无法访问源目录: {source_dir}")
        return

    logging.info(f"🚀 开始扫描 (支持本地进度续传)...")

    for root, dirs, files in os.walk(source_dir):
        # 【核心续传优化】:如果当前目录在已处理清单中,直接跳过
        if root in processed_dirs:
            folder_skipped_count += 1
            continue

        # 控制台打印时替换畸形字符,防止控制台崩溃
        safe_root = root.encode('utf-8', 'replace').decode('utf-8')
        print(f"🔍 正在扫描新目录: {safe_root}", end='\r') 
        
        dir_has_old_files = False
        
        for file in files:
            file_path = os.path.join(root, file)
            relative_path = os.path.relpath(file_path, source_dir)
            target_file_path = os.path.join(target_dir, relative_path)

            max_retries = 3
            for attempt in range(max_retries):
                try:
                    if os.path.exists(target_file_path):
                        skipped_count += 1
                        break

                    target_sub_dir = os.path.dirname(target_file_path)
                    if not os.path.exists(target_sub_dir):
                        os.makedirs(target_sub_dir, exist_ok=True)

                    file_mtime = os.path.getmtime(file_path)
                    if file_mtime >= threshold_time:
                        break

                    shutil.copy2(file_path, target_file_path)
                    os.remove(file_path)
                    
                    moved_count += 1
                    dir_has_old_files = True
                    safe_rel_path = relative_path.encode('utf-8', 'replace').decode('utf-8')
                    print(f"✅ 已移动: {safe_rel_path}   ") 
                    break

                except OSError as e:
                    # 针对网络错误 (53, 59, 64) 进行重试
                    if hasattr(e, 'winerror') and e.winerror in (59, 53, 64) and attempt < max_retries - 1:
                        wait_time = (attempt + 1) * 10  # 网络错误等待时间加长
                        logging.warning(f"⚠️ 网络波动,{wait_time}秒后重试...")
                        time.sleep(wait_time)
                        continue
                    else:
                        safe_rel_path = relative_path.encode('utf-8', 'replace').decode('utf-8')
                        logging.error(f"❌ 移动失败: {safe_rel_path}, 错误: {e}")
                        error_count += 1
                        break
                except Exception as e:
                    safe_rel_path = relative_path.encode('utf-8', 'replace').decode('utf-8')
                    logging.error(f"❌ 未知错误: {safe_rel_path}, 错误: {e}")
                    error_count += 1
                    break
        
        # 【关键步骤】:当前文件夹处理完毕后,将其加入已处理清单并安全保存
        processed_dirs.add(root)
        save_progress(processed_dirs)

    print("\n" + "="*50)
    logging.info("🏁 任务结束!")
    logging.info(f"成功移动: {moved_count} 个 | 单文件跳过: {skipped_count} 个 | 整个目录跳过: {folder_skipped_count} 个 | 失败: {error_count} 个")

if __name__ == "__main__":
    source_directory = r"\path\to\your\source\folder"
    target_directory = r"\path\to\your\target\folder"

    print(f"正在处理: {source_directory}")
    move_old_files_safely(source_directory, target_directory)
心海质水
rungod
帖子: 63
注册时间: 2010-06-19 3:51

python将指定目录下指定年限以前的所有文件转移到指定的目录

帖子 rungod »

代码: 全选

import os
import shutil
import time

def move_old_files(source_dir, target_dir, years=2):
    """
    将 source_dir 中超过指定年份的文件(含子目录)剪切到 target_dir
    :param source_dir: 源目录路径
    :param target_dir: 目标目录路径
    :param years: 年份阈值,默认为2年
    """
    # 计算两年前的时间戳
    current_time = time.time()
    threshold_time = current_time - (years * 365 * 24 * 60 * 60)

    moved_count = 0
    for root, dirs, files in os.walk(source_dir):
        for file in files:
            file_path = os.path.join(root, file)
            
            # 获取文件的最后修改时间
            try:
                file_mtime = os.path.getmtime(file_path)
            except OSError:
                continue  # 如果无法获取文件时间,跳过

            # 判断文件是否早于两年前的时间点
            if file_mtime < threshold_time:
                # 计算相对路径,以便在目标目录中重建相同的子目录结构
                relative_path = os.path.relpath(file_path, source_dir)
                target_file_path = os.path.join(target_dir, relative_path)
                
                # 确保目标子目录存在
                target_sub_dir = os.path.dirname(target_file_path)
                if not os.path.exists(target_sub_dir):
                    os.makedirs(target_sub_dir)

                # 执行剪切(移动)操作
                try:
                    shutil.move(file_path, target_file_path)
                    moved_count += 1
                    print(f"已移动: {relative_path}")
                except Exception as e:
                    print(f"移动失败: {relative_path}, 错误: {e}")

    print(f"\n操作完成!共移动了 {moved_count} 个文件到 {target_dir}")

# --- 使用示例 ---
if __name__ == "__main__":
    # 1. 修改为你需要处理的源目录
    source_directory = r"C:\path\to\your\source\folder"
    # 2. 修改为你想要移动到的目标目录
    target_directory = r"C:\path\to\your\target\folder"

    if not os.path.exists(source_directory):
        print("错误:源目录不存在,请检查路径!")
    else:
        move_old_files(source_directory, target_directory)
心海质水
回复