前言
我们的一些数据很珍贵,比如实验处理的中间结果,比如一些编辑的文件。 有时候我们会备份到不止一个地方(比如我,会备份到不同的u盘或者移动硬盘),但我们有可能某个时间段在某一份更改了好多次操作,我们自己也不知道改了哪些,这时候如果想备份的话有两种方法。第一,一个地方一个地方找,更要命的是多个备份的地方都要改。第二种方法是把旧的删了,新的重新复制,虽然简单粗暴,但是单文件很多很大时,更改的又不是很多,那么这样子每次都复制这么多个文件明显划不来。 (另外,大文件情况下,git就不是特别好用,传到云端备份再回来网速不好就很难受。
正是有这样想法, 让我想要写一个可以同步两个目录的程序。 基于Python,但是目前有个缺点就是是单线程的,可能今后有空想想怎么异步优化其性能。
使用一定要注意,是a -> b, 别反过来 !!! (不然会把旧版本目录覆盖掉新版本目录
代码
"""
大文件以及包含大文件的目录重命名建议直接手动操作
因为本代码只要名字不同都认为是不同的文件,没有做内容比较的优化
a -> b, 由 a 目录向 b 目录备份
Author: Andy Dennis AIron
Date: 2021.11.11
Version: V3.0
Detail: 相比V2.0, 修复exFAT格式文件系统时间戳精度问题。(line 84 判断条件)
判断文件是否相同方法: 文件大小和修改时间戳区别是否大于2秒, 所以请您修改完文件选择保存
的时间 与 上一次修改文件的时间(备份时的那个版本的文件)大于2秒即可
"""
import shutil
import os
from tqdm import tqdm
class Sync:
def __init__(self, d_a:str, d_b:str):
"""
同步目录: d_a -> d_b
"""
self.add_dir_lt = []
self.del_dir_lt = []
self.add_file_lt = []
self.edit_file_lt = []
self.del_file_lt = []
self.dir_origin = d_a
self.dir_target = d_b
self.compare_directory(d_a=d_a, d_b=d_b)
flag = self.show_tip_ask()
if flag == 'yes':
self.start_operation()
elif flag == 'useless':
print(' 两个目录已经是相同的啦, 您不需要再进行操作~~~')
else:
print('您取消了操作...')
def compare_directory(self, d_a:str, d_b:str):
d_a_set = set(os.listdir(d_a))
d_b_set = set(os.listdir(d_b))
d_a_more_item_lt = []
d_a_more_item_lt = []
path_same_item_lt = []
d_a_more_item_lt = list(d_a_set - d_b_set)
d_b_more_item_lt = list(d_b_set - d_a_set)
path_same_item_lt = list(d_a_set & d_b_set)
for item in d_a_more_item_lt:
item_path = '{}/{}'.format(d_a, item)
if os.path.isdir(item_path):
self.add_dir_lt.append(item_path.replace(self.dir_origin, self.dir_target))
self.add_all_director_item(item_path, True)
else:
self.add_file_lt.append(item_path)
for item in d_b_more_item_lt:
item_path = '{}/{}'.format(d_b, item)
if os.path.isdir(item_path):
self.del_dir_lt.append(item_path)
self.add_all_director_item(item_path, False)
else:
self.del_file_lt.append(item_path)
for item in path_same_item_lt:
item_a_path = '{}/{}'.format(d_a, item)
item_b_path = '{}/{}'.format(d_b, item)
if os.path.isdir(item_a_path):
self.compare_directory(d_a=item_a_path, d_b=item_b_path)
else:
time_a = os.path.getmtime(item_a_path)
time_b = os.path.getmtime(item_b_path)
if os.path.getsize(item_a_path) == os.path.getsize(item_b_path) and \
abs(time_a - time_b) <= 2:
pass
else:
self.edit_file_lt.append([item_a_path, item_b_path])
def add_all_director_item(self, dir_path:str, is_add:bool):
for item in os.listdir(dir_path):
item_path = '{}/{}'.format(dir_path, item)
if os.path.isdir(item_path):
if is_add:
self.add_dir_lt.append(item_path.replace(self.dir_origin, self.dir_target))
self.add_all_director_item(item_path, is_add)
else:
if is_add:
self.add_file_lt.append(item_path)
else:
self.del_file_lt.append(item_path)
def show_tip_ask(self):
print('原始目录: ', self.dir_origin)
print('目标目录: ', self.dir_target)
if len(self.add_dir_lt) == 0 and len(self.del_dir_lt) == 0 and \
len(self.add_file_lt) == 0 and len(self.del_file_lt) == 0 and \
len(self.edit_file_lt) == 0:
return 'useless'
print('执行完操作后 目标目录 将与 原始目录一样\n')
self.print_operation_lt(self.add_dir_lt, '要创建的目录')
self.print_operation_lt(self.del_dir_lt, '要删除的目录')
self.print_operation_lt(self.add_file_lt, '要复制的文件')
self.print_operation_lt(self.del_file_lt, '要删除的文件')
self.print_operation_lt(self.edit_file_lt, '要修改的文件')
choice = input('请您仔细查看后,确认是否继续操作(y/n)')
if choice.isalpha() and choice.lower() == 'y':
return 'yes'
return 'false'
def print_operation_lt(self, lt:list, tip:str):
if len(lt) > 0:
print(' {} '.format(tip).center(50, '-'))
for item in lt:
if isinstance(item , list):
print(' {} -> {}'.format(item[0], item[1]))
else:
print(' ' * 4, item)
print()
def start_operation(self):
print('\n ^_^开始操作...')
if len(self.add_dir_lt) > 0:
print('正在创建目录...')
for item in tqdm(self.add_dir_lt):
os.makedirs(item)
print()
if len(self.add_file_lt) > 0:
print('正在复制文件...')
for item in tqdm(self.add_file_lt):
item_target = item.replace(self.dir_origin, self.dir_target)
shutil.copyfile(item, item_target)
self.set_a_m_time(item, item_target)
print()
if len(self.del_file_lt) > 0:
print('正在删除文件...')
for item in tqdm(self.del_file_lt):
os.remove(item)
print()
if len(self.edit_file_lt) > 0:
print('正在修改文件...')
for item in tqdm(self.edit_file_lt):
os.remove(item[1])
shutil.copyfile(item[0], item[1])
self.set_a_m_time(item[0], item[1])
print()
if len(self.del_dir_lt) > 0:
print('正在删除目录...')
for item in tqdm(self.del_dir_lt):
shutil.rmtree(item)
print(' 执行结束 done!')
def set_a_m_time(self, item_o, item_t):
st_item_o = os.stat(item_o)
os.utime(item_t, (st_item_o.st_atime, st_item_o.st_mtime))
if __name__ == '__main__':
d1 = 'C:/Users/Administrator/Desktop/sync/a'
d2 = 'C:/Users/Administrator/Desktop/sync/b'
_ = Sync(d1, d2)
效果
先来一个简单的实验
再来一个复杂一点的
如果路径一样:
|