使用Python同步文件夹

  同步文件夹的工具有很多,下面是我用 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 中。

  程序代码如下:

# -*- coding: utf-8 -*-
# http://oldj.net/

u"""
同步两个文件夹

用法:

python syncdir.py source_dir target_dir

执行后,source_dir 中的文件将被同步到 target_dir 中
这个同步是单向的,即只将 source_dir 中更新或新增的文件拷到 target_dir 中,
如果某个文件在 source_dir 中不存在而在 target_dir 中存在,本程序不会删除那个文件,
也不会将其拷贝到 source_dir 中

判断文件是否更新的方法是比较文件最后修改时间以及文件大小是否一致
"""

import os
import sys
import shutil

def errExit(msg):
	print "-" * 50
	print "ERROR:"
	print msg
	sys.exit(1)

def main(source_dir, target_dir):
	print "synchronize '%s' >> '%s'..." % (source_dir, target_dir)
	print "=" * 50
	sync_file_count = 0
	sync_file_size = 0

	for root, dirs, files in os.walk(source_dir):
		relative_path = root.replace(source_dir, "")
		if len(relative_path) > 0 and relative_path[0] in ("/", "\\"):
			relative_path = relative_path[1:]
		dist_path = os.path.join(target_dir, 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
				print "copying '%s' ..." % fn0
				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
	print "done!"

if __name__ == "__main__":
	if len(sys.argv) != 3:
		if "-h" in sys.argv or "--help" in sys.argv:
			print __doc__
			sys.exit(1)
		errExit(u"invalid arguments!")
	source_dir, target_dir = sys.argv[1:]
	if os.path.isdir(source_dir) == False:
		errExit(u"'%s' is not a folder!" % source_dir)
	elif os.path.isdir(target_dir) == False:
		errExit(u"'%s' is not a folder!" % target_dir)

	main(source_dir, target_dir)


目前这个版本只支持本机不同文件夹下的同步,不支持远程网络同步。这段脚本的编写过程中,我发现其中有几点需要注意。

  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 有可能会有微小的不一致,所以,脚本中我用了四舍五入,只保留文件最后修改时间小数点后的三位(即精确到微秒)。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值