同步文件夹的工具有很多,下面是我用 Python 写的一个小脚本,兼容 Windows 和 Linux,算是重复发明轮子,不过也当是练习,呵呵。用法很简单,如下:
python syncdir.py source_dir target_dir
作用是将文件夹 source_dir 中的文件同步到文件夹 target_dir 中,同步的过程遵循以下规则:
1、如果文件 f1 在 source_dir 中存在,且不在 target_dir 中,则将 f1 拷到 target_dir 中;
2、如果文件 f1 在 source_dir 与 target_dir 中都存在,但最后修改时间不一样或文件大小不一样,则将 f1 从 source_dir 拷到 target_dir 中,覆盖 target_dir 中的原文件。
可以看到,这个规则非常简单,而且是单向的,即只保证同步之后 source_dir 中的文件在 target_dir 中保持一致,但 target_dir 中独有的文件不会反向同步到 source_dir 中。
程序代码如下:
import sys
import os
import os.path
import shutil
import re
def listAndCompareFile(localSourcePath,localCachePath):
sync_file_count = 0
sync_file_size=0
for root,dirs,files in os.walk(localSourcePath):
relative_path=root.replace(localSourcePath,"")
if len(relative_path)>0:
relative_path = relative_path[1:]
dist_path=os.path.join(localCachePath,relative_path)
if os.path.isdir(dist_path) == False:
os.makedirs(dist_path)
last_copy_folder = ""
for fn0 in files:
fn=os.path.join(root,fn0)
fn2=os.path.join(dist_path,fn0)
is_copy=False
if os.path.isfile(fn2) ==False:
is_copy = True
else:
statinfo=os.stat(fn)
statinfo2=os.stat(fn2)
is_copy = (round(statinfo.st_mtime,3) != round(statinfo2.st_mtime,3)
or statinfo.st_size != statinfo2.st_size)
if is_copy:
if dist_path != last_copy_folder:
print '[%s]' % dist_path
last_copy_folder=dist_path
shutil.copy2(fn,fn2)
sync_file_count += 1
sync_file_size += os.stat(fn).st_size
if sync_file_count >0:
print "-"*50
print "%d files synchronized!" % sync_file_count
if sync_file_size >0:
print "%d bytes." % sync_file_size
if __name__ == "__main__":
localCachePath = "/root/sw/cache"
localSourcePath="/root/sw/study"
if os.path.isdir(localCachePath) == False:
os.mkdir(localCachePath)
else:
print "Unkown error"
listAndCompareFile(localSourcePath,localCachePath)
这个版本只支持本机不同文件夹下的同步,不支持远程网络同步。这段脚本的编写过程中,我发现其中有几点需要注意。
1、在 Windows 下,os.path.join 的第一个参数如果以“:”结尾,得到的结果可能不是我们想要的。比如:os.path.join(“D:”, “tmp”),我们可能会预期得到 “D:\tmp” ,但实际得到的是 “D:tmp” ,所以这样的情况下需要使用 os.sep.join(“D:”, “tmp”) 。
2、os.path.join 的第二个参数如果以“/”开头(Windows 下如果以“/”或“”开头),则第一个参数将被忽略。比如 os.path.join(“tmp”, “/test”) 得到的将是 “/test” 。
3、shutil.copy2 可以将文件拷贝到指定地方,它不仅拷贝文件内容,同时还会把文件的创建时间、最后修改时间等信息一起拷过去。
3、用 os.stat(filename).st_mtime 可以取到指定文件的最后修改时间,这个时间是一个浮点小数。用上面的 shutil.copy2 拷贝的文件虽然能保留文件的最后修改时间,但有时由于浮点精度的问题,可能在小数的最后一两位会有误差。因此,一次同步过后,两个相同文件的 st_mtime 有可能会有微小的不一致,所以,脚本中我用了四舍五入,只保留文件最后修改时间小数点后的三位(即精确到微秒)。
当然,这个脚本有点小长,如果需要的话可以精简得小一点,不过仅仅压缩代码行数而不提升程序效率貌似意义不大,所以暂时先这样用着,将来有需要时继续研究。