Function list:
- 📁 支持文件和目录的移动/复制
- ⏰ 支持 cron 定时任务
- 📝 基于 JSON 配置文件
- 🔄 支持覆盖已存在的文件
- 📋 支持批量操作
requirements.txt
APScheduler==3.11.1
config.json
filename: 文件名(可选,如果 source_path 是目录则必需)
source_path: 源文件/目录路径
destination_path: 目标路径
overwrite: 是否覆盖已存在的文件(0=否,1=是)
copy_or_remove: 操作模式("copy"=复制保留源文件,"remove"=移动删除源文件)
cron: 定时任务表达式(可选,格式:分 时 日 月 周 或 秒 分 时 日 月 周)
{
"files_to_move": [
{
"filename": "test.txt",
"source_path": "C:/Users/87104/Desktop/code/move-file/test file",
"destination_path": "C:/Users/87104/Desktop/code/move-file/test file/archive",
"overwrite": 0,
"copy_or_remove": "copy",
"cron": "11 * * * *"
},
{
"filename": "test1.txt",
"source_path": "C:/Users/87104/Desktop/code/move-file/test file",
"destination_path": "C:/Users/87104/Desktop/code/move-file/test file/archive",
"overwrite": 1,
"copy_or_remove": "remove",
"cron": "11 * * * *"
}
]
}
main.py
"""
文件移动工具
从配置文件读取文件列表并执行移动操作
"""
import shutil
import sys
import json
import time
from pathlib import Path
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.triggers.cron import CronTrigger
CONFIG_FILE = 'config.json'
def load_config():
"""
加载配置文件
Returns:
dict: 配置字典,包含 files_to_move 列表
"""
default_config = {"files_to_move": []}
if not Path(CONFIG_FILE).exists():
return default_config
try:
with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
config = json.load(f)
if "files_to_move" not in config:
config["files_to_move"] = []
return config
except (json.JSONDecodeError, IOError):
return default_config
def execute_single_file(file_info):
"""
执行单个文件的移动或复制操作
Args:
file_info (dict): 文件配置信息
"""
filename = file_info.get("filename", "")
source = file_info.get("source_path", "")
destination = file_info.get("destination_path", "")
if not source or not destination:
print(f"跳过: 配置信息不完整 - {filename}")
return False
file_overwrite = bool(file_info.get("overwrite", 0))
copy_or_remove = file_info.get("copy_or_remove", "remove")
is_copy = copy_or_remove.lower() == "copy"
source_path = Path(source)
if source_path.exists() and source_path.is_dir() and filename:
actual_source = source_path / filename
if not actual_source.exists():
print(f"跳过: 文件不存在 - {filename}")
return False
source = str(actual_source)
display_name = filename
else:
display_name = filename if filename else source_path.name
operation = "复制" if is_copy else "移动"
print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 正在{operation}: {display_name}")
print(f" 源路径: {source}")
print(f" 目标路径: {destination}")
print(f" 覆盖模式: {'是' if file_overwrite else '否'}")
print(f" 操作模式: {copy_or_remove}")
success = move_file(source, destination, file_overwrite, is_copy)
print()
return success
def execute_from_config():
"""
从配置文件读取待移动文件列表并执行移动
Returns:
bool: 是否所有文件都移动成功
"""
config = load_config()
files_to_move = config.get("files_to_move", [])
if not files_to_move:
print("配置文件中没有待移动的文件")
return False
print(f"从配置文件中读取到 {len(files_to_move)} 个待移动文件\n")
success_count = sum(1 for file_info in files_to_move if execute_single_file(file_info))
fail_count = len(files_to_move) - success_count
print(f"\n移动完成: 成功 {success_count} 个, 失败 {fail_count} 个")
return fail_count == 0
def move_file(source, destination, overwrite=False, is_copy=False):
"""
移动或复制文件或目录
Args:
source (str): 源文件/目录路径
destination (str): 目标路径
overwrite (bool): 是否覆盖已存在的文件
is_copy (bool): 是否复制。True=复制(保留源文件),False=移动(删除源文件)
Returns:
bool: 操作是否成功
"""
try:
source_path = Path(source).resolve()
dest_path = Path(destination).resolve()
if not source_path.exists():
print(f"错误: 源路径不存在: {source}")
return False
if dest_path.exists() and dest_path.is_dir():
dest_path = dest_path / source_path.name
if dest_path.exists():
if not overwrite:
print(f"错误: 目标路径已存在: {dest_path}")
print("提示: 在配置文件中设置 overwrite 为 1 可以覆盖已存在的文件")
return False
if dest_path.is_dir():
shutil.rmtree(dest_path)
else:
dest_path.unlink()
if is_copy:
if source_path.is_dir():
shutil.copytree(str(source_path), str(dest_path))
else:
shutil.copy2(str(source_path), str(dest_path))
print(f"成功: 已将 '{source_path}' 复制到 '{dest_path}'")
else:
shutil.move(str(source_path), str(dest_path))
print(f"成功: 已将 '{source_path}' 移动到 '{dest_path}'")
return True
except PermissionError:
print(f"错误: 没有权限访问文件: {source}")
return False
except Exception as e:
operation = "复制" if is_copy else "移动"
print(f"错误: {operation}文件时发生异常: {e}")
return False
def setup_scheduler(files_to_move):
"""
设置定时任务调度器
Args:
files_to_move (list): 文件配置列表
Returns:
BlockingScheduler: 调度器对象,如果没有有效任务则返回 None
"""
scheduler = BlockingScheduler(timezone='Asia/Shanghai')
scheduled_count = 0
for i, file_info in enumerate(files_to_move, 1):
cron = file_info.get("cron", "")
if not cron:
continue
try:
cron_parts = cron.strip().split()
if len(cron_parts) == 5:
minute, hour, day, month, day_of_week = cron_parts
trigger = CronTrigger(minute=minute, hour=hour, day=day, month=month, day_of_week=day_of_week, timezone='Asia/Shanghai')
elif len(cron_parts) == 6:
second, minute, hour, day, month, day_of_week = cron_parts
trigger = CronTrigger(second=second, minute=minute, hour=hour, day=day, month=month, day_of_week=day_of_week, timezone='Asia/Shanghai')
else:
print(f"警告: 文件 {i} 的 cron 格式不正确,跳过: {cron}")
continue
filename = file_info.get("filename", f"文件{i}")
job = scheduler.add_job(execute_single_file, trigger=trigger, args=[file_info], id=f"file_{i}", name=f"移动文件: {filename}")
scheduled_count += 1
next_run = job.next_run_time.strftime('%Y-%m-%d %H:%M:%S') if job.next_run_time else "未知"
print(f"已设置定时任务: {filename} - cron: {cron} - 下次执行: {next_run}")
except Exception as e:
print(f"警告: 文件 {i} 的 cron 配置错误,跳过: {e}")
import traceback
traceback.print_exc()
if scheduled_count == 0:
return None
print(f"\n共设置了 {scheduled_count} 个定时任务")
print(f"当前时间: {time.strftime('%Y-%m-%d %H:%M:%S')}")
print("定时任务已启动,等待执行...\n")
return scheduler
def main():
"""
主函数
从配置文件读取并执行文件移动操作
支持定时任务模式
"""
config = load_config()
files_to_move = config.get("files_to_move", [])
if not files_to_move:
print("配置文件中没有待移动的文件")
sys.exit(1)
has_cron = any(file_info.get("cron", "") for file_info in files_to_move)
if has_cron:
print("启动定时任务模式...\n")
scheduler = setup_scheduler(files_to_move)
if scheduler:
print("=" * 50)
print("调度器已启动,程序将持续运行")
print("按 Ctrl+C 可以停止定时任务")
print("=" * 50 + "\n")
try:
scheduler.start()
except KeyboardInterrupt:
print("\n定时任务已停止")
scheduler.shutdown()
else:
print("没有有效的定时任务,执行一次立即执行模式")
execute_from_config()
else:
success = execute_from_config()
sys.exit(0 if success else 1)
if __name__ == '__main__':
main()