#!/usr/bin/python3
#_*_ coding:utf8 _*_
## V 1.1
## 增量备份目录中的文件
## 当文件修改时间改变时备份文件
## python 字典变量作为文件信息数据库 {文件ID:(修改时间, 文件路径), }
## Windows使用 '\\' 作为路径分割,Linux需要修改为 '/'
import os
import time
import pickle
import shutil
import logging # 日志模块
LOG_FILE = time.strftime('%Y%m%d')+'.log' # 日志文件格式,每天一个文件
Log = logging.getLogger('__name__') # 获取实例
formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s') # 指定logger输出格式
file_handler = logging.FileHandler(LOG_FILE) # 日志文件路径
file_handler.setFormatter(formatter) # 可以通过setFormatter指定输出格式
Log.addHandler(file_handler) # 为logger添加的日志处理器
# 设置记录的日志级别
Log.setLevel(logging.DEBUG)
#Log.setLevel(logging.INFO)
#Log.setLevel(logging.WARNING)
#Log.setLevel(logging.ERROR)
#Log.setLevel(logging.CRITICAL)
## 同时在终端显示
console = logging.StreamHandler()
console.setLevel(logging.DEBUG)
Log.addHandler(console)
'''
参考资料
文件时间
os.stat('A:\\a.txt')
os.stat_result(st_mode=33206, st_ino=6755399441061923, st_dev=3365460235, st_nlink=1, st_uid=0, st_gid=0, st_size=11, st_atime=1650868975, st_mtime=1650868975, st_ctime=1650868939)
os.stat('A:\\a.txt')
os.stat_result(st_mode=33206, st_ino=6755399441061923, st_dev=3365460235, st_nlink=1, st_uid=0, st_gid=0, st_size=30, st_atime=1650937382, st_mtime=1650937382, st_ctime=1650868939)
在windows下一个文件有三种时间属性:
1>创建时间
2>修改时间
3>访问时间
相似的在Linux下一个文件也有三种时间属性:(与windows不同的是linux没有创建时间,而多了个访问时间)
1>访问时间(access time 简写为atime)
2>修改时间(modify time 简写为mtime)
3>状态修改时间(change time 简写为ctime)
关于Linux底下三种时间的简单介绍:
atime:(access time)显示的是文件中的数据最后被访问的时间,比如系统的进程直接使用或通过一些命令和脚本间接使用。(执行一些可执行文件或脚本)
mtime:(modify time)显示的是文件内容被修改的最后时间,比如用vi编辑时就会被改变。(也就是Block的内容)
ctime:(change time)显示的是文件的权限、拥有者、所属的组、链接数发生改变时的时间。当然当内容改变时也会随之改变(即inode内容发生改变和Block内容发生改变时)
'''
## 增量备份的基础是先全备份
def 全备份(DIR_PATH, DIR_BACKUP_ROOT, DIR_BACKUP):
if not os.path.exists(DIR_BACKUP_ROOT):
os.makedirs(DIR_BACKUP_ROOT)
shutil.copytree(DIR_PATH, DIR_BACKUP_ROOT+DIR_BACKUP) # 复制整个目录(备份)
## Python变量保存到文件
def PKL_SAVE(X, FILE_PKL):
f = open(FILE_PKL, 'wb')
pickle.dump(X, f)
f.close()
## 从文件读取Python变量
def PKL_LOAD(FILE_PKL):
f = open(FILE_PKL, 'rb')
X = pickle.load(f)
f.close()
return(X)
## 对文件操作:记录文件被修改时间
def FILE_记录(FILE_PATH, D_FILE_MTIME):
FS = os.stat(FILE_PATH)
FILE_ID = FS.st_ino # 文件ID
FILE_SIZE = FS.st_size # 文件大小
FILE_MTIME = FS.st_mtime # 文件修改时间
#print(f"文件 {FILE_ID:16} 修改时间={FILE_MTIME:.8f} {FILE_PATH}")
D_FILE_MTIME[FILE_ID] = (FILE_MTIME, FILE_PATH) ## 记录文件修改时间和文件全路径
if FILE_SIZE == 0:
INFO = f"空文件 {FILE_PATH}"
Log.info(INFO)
## 对目录操作:遍历
def DIR_遍历(DIR_PATH, D_FILE_MTIME, 目录分割符):
L = os.listdir(DIR_PATH)
for i in L: # 获取目录里面的内容列表
if os.path.isdir(DIR_PATH+i): # 如果还是目录
DIR_遍历(DIR_PATH+i+目录分割符, D_FILE_MTIME, 目录分割符) # 循环迭代进入目录
elif os.path.isfile(DIR_PATH+i): # 如果是文件
FILE_记录(DIR_PATH+i, D_FILE_MTIME) # 对文件进行操作
## 自定义备份文件名样式
def 备份文件命名(DIR_SAVE, FILE_PATH, 目录分割符):
备份全文件名 = DIR_SAVE+time.strftime('%Y%m%d%H%M%S')+FILE_PATH.replace(':', '').replace(目录分割符, '__')
return(备份全文件名)
## 主函数
def RUN(DIR_PATH, FILE_PKL, DIR_BACKUP_ROOT, DIR_BACKUP, 增量备份目录, 目录分割符):
if os.path.isdir(DIR_PATH):
## 全备份
if not os.path.isdir(DIR_BACKUP_ROOT+DIR_BACKUP):
全备份(DIR_PATH, DIR_BACKUP_ROOT, DIR_BACKUP)
INFO = f"全备份{DIR_BACKUP_ROOT}{DIR_BACKUP}"
Log.info(INFO)
## 增量备份
if not os.path.exists(FILE_PKL):
D_FILE_MTIME_INIT = {}
DIR_遍历(DIR_PATH, D_FILE_MTIME_INIT, 目录分割符)
PKL_SAVE(D_FILE_MTIME_INIT, FILE_PKL)
INFO = f"记录文件数量(始)={len(D_FILE_MTIME_INIT)}"
Log.info(INFO)
else:
## 读取上一次文件修改时间信息
D_FILE_MTIME_OLD = PKL_LOAD(FILE_PKL)
INFO = f"记录文件数量(旧)={len(D_FILE_MTIME_OLD)}"
Log.info(INFO)
## 记录本次文件修改时间信息
D_FILE_MTIME_NEW = {}
DIR_遍历(DIR_PATH, D_FILE_MTIME_NEW, 目录分割符)
PKL_SAVE(D_FILE_MTIME_NEW, FILE_PKL) # 更新 FILE_PKL 文件
INFO = f"记录文件数量(新)={len(D_FILE_MTIME_NEW)} 更新 FILE_PKL 文件"
Log.info(INFO)
## 新文件信息对比旧文件信息,忽略被删除的情况
## 文件被修改就备份一下
if not os.path.exists(增量备份目录):
os.makedirs(增量备份目录)
for FILE_ID in D_FILE_MTIME_NEW:
FILE_MTIME_NEW, FILE_PATH_NEW = D_FILE_MTIME_NEW[FILE_ID] # 当前检测到的文件的ID,修改时间,文件路径
if FILE_ID in D_FILE_MTIME_OLD: # 新文件有旧记录
FILE_MTIME_OLD, FILE_PATH_OLD = D_FILE_MTIME_OLD[FILE_ID] # 上次检测到的文件的ID,修改时间,文件路径
if FILE_MTIME_NEW != FILE_MTIME_OLD: # 文件修改时间有变化,说明文件内容已改变
## 备份被修改的文件
FILE_PATH_SAVE = 备份文件命名(增量备份目录, FILE_PATH_NEW, 目录分割符)
shutil.copy(FILE_PATH_NEW, FILE_PATH_SAVE) # 复制并重命名新文件
## 判断是否被重命名
if FILE_PATH_NEW != FILE_PATH_OLD:
INFO = f"被修改 {FILE_PATH_OLD} 重命名或移动 {FILE_PATH_NEW} 【备份为】 {FILE_PATH_SAVE}"
Log.info(INFO)
else:
INFO = f"被修改 {FILE_PATH_NEW} 【备份为】 {FILE_PATH_SAVE}"
Log.info(INFO)
else: # 文件修改时间没有变,说明文件内容未改变
## 判断是否被重命名
if FILE_PATH_NEW != FILE_PATH_OLD:
INFO = f"重命名 {FILE_PATH_OLD} 重命名或移动 {FILE_PATH_NEW}"
Log.info(INFO)
else:
INFO = f"无变化 {FILE_PATH_NEW}"
Log.info(INFO)
else: # 新文件无旧记录,新增文件
FILE_PATH_SAVE = 备份文件命名(增量备份目录, FILE_PATH_NEW, 目录分割符)
shutil.copy(FILE_PATH_NEW, FILE_PATH_SAVE) # 复制并重命名新文件
INFO = f"新增 {FILE_PATH_NEW} 【备份为】 {FILE_PATH_SAVE}"
Log.info(INFO)
## 被删除的情况
for FILE_ID in D_FILE_MTIME_OLD:
FILE_MTIME_OLD, FILE_PATH_OLD = D_FILE_MTIME_OLD[FILE_ID] # 上次检测到的文件的ID,修改时间,文件路径
if FILE_ID not in D_FILE_MTIME_NEW: # 旧文件ID在新记录中找不到
INFO = f"被删除 {FILE_PATH_OLD}"
Log.info(INFO)
else:
ERROR = f"要备份的目录: {DIR_PATH} 不存在"
Log.error(ERROR)
## 运行
## 根据系统类型设置目录分割符
if os.name == 'posix': # Linux类型系统
目录分割符 = '/'
else:
目录分割符 = '\\'
# Windows设置
DIR_PATH = 'A:\\TEST_DIR\\' # 要备份的目录
FILE_PKL = 'A:\\D.pkl' # 文件信息数据库(Python字典变量 {文件ID:(修改时间, 文件路径), })
DIR_BACKUP_ROOT = 'A:\\BACKUP\\' # 全备份主目录
DIR_BACKUP = time.strftime('%Y%m%d') # 全备份子目录(每天一个全备份子目录)
增量备份目录 = 'A:\\BACKUP_ADD\\' # 增量备份目录
# Linux 设置
#DIR_PATH = '/TEST_DIR/'
#FILE_PKL = 'D.pkl'
#DIR_BACKUP_ROOT = '/BACKUP/'
#DIR_BACKUP = time.strftime('%Y%m%d')
#增量备份目录 = '/BACKUP_ADD/'
RUN(DIR_PATH, FILE_PKL, DIR_BACKUP_ROOT, DIR_BACKUP, 增量备份目录, 目录分割符)
Python3增量备份目录中的文件(读取文件修改时间方式)
于 2022-05-16 11:24:08 首次发布
这是一个使用Python编写的增量备份脚本,它会监测指定目录中的文件变化,并在文件内容修改时进行备份。脚本首先进行全备份,然后通过记录和比较文件的修改时间来确定哪些文件需要增量备份。日志模块用于记录操作详情,支持在终端和日志文件中显示。此脚本适用于Linux和Windows系统。
摘要由CSDN通过智能技术生成