forked from akkana/scripts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pysync
executable file
·130 lines (108 loc) · 4.26 KB
/
pysync
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#! /usr/bin/env python
# Wrapper for rsync that adds several features:
# 1. preserves atime on the source,
# so e.g. you can back up your mutt folder directory without
# messing up mutt's idea of which folders have new mail).
# TODO: preserve it on the dest end too.
# 2. Print the list of what will be copied, give the user a chance
# to confirm, then do it.
# 3. Preserve password for remote copies, so you only have to type
# it once, not several times.
# Rsync has two ways to do that, os.environ["RSYNC_PASSWORD"] and
# --password-file=FILE, but they only work with rsyncd, not with ssh.
# So use pexpect instead.
# Copyright 2011 by Akkana Peck. Share and enjoy under the GPL v.2 or later.
import sys, os, getpass, pexpect
check_cmd = "rsync -avn --delete %s %s"
sync_cmd = "rsync -av --delete %s %s"
def Usage() :
print "Usage: %s source dest" % sys.argv
sys.exit(1)
# atime and mtime for every file under src:
utimes = {}
def find_all_atimes(src) :
print "Saving all atimes"
for root, dirs, files in os.walk(src) :
for file in files :
path = os.path.join(root, file)
#atimes[file] = os.path.getatime(path)
filestat = os.stat(path)
utimes[path] = ( filestat.st_atime, filestat.st_mtime )
def find_atimes(files) :
print "Saving all atimes"
for file in files :
#atimes[file] = os.path.getatime(os.path.join(root, file))
filestat = os.stat(file)
utimes[file] = ( filestat.st_atime, filestat.st_mtime )
def restore_all_atimes(src) :
print "Restoring all atimes and mtimes: ",
for file in utimes.keys() :
print "\rRestoring", file, #'\tto', utimes[file],
os.utime(file, utimes[file])
print "\nRestored all atimes and mtimes"
def sync(src, dst, confirm=True) :
# Find all atimes BEFORE calling rsync, since rsync will destroy them:
find_all_atimes(src)
passwd = None
if confirm :
# use rsync to get the list of files needing sync
print "Getting list of files that need to be transfered"
deletefiles = []
copyfiles = []
child = pexpect.spawn(check_cmd % (src, dst))
# rsync will send as its first line either a password: prompt,
# or , if it needs no passwd, "sending incremental file list".
# If we expect either one we can detect whether a password
# is needed.
got = child.expect(['password:', 'sending incremental file list'])
if got == 0 : # a password prompt
passwd = getpass.getpass("Password for %s: " % dst)
child.sendline (passwd)
print "Sent password"
else : # sent file list without needing password
print "No password needed"
while True :
line = child.readline()
if not line : break
line = line.strip()
if not line or line.startswith("building file list") \
or line.startswith("sending ") or line.startswith("created ") \
or line.startswith("sent ") or line.startswith("total ") \
or line == './' :
continue
elif line.startswith("deleting ") :
deletefiles.append(line[9:])
else :
copyfiles.append(line)
child.close()
print "Files to delete:"
for f in deletefiles :
print "-", f
print "\nFiles to copy:"
for f in copyfiles :
print "+", f
# We could probably replace utimes list with the list
# of files actually copied -- does rsync change atime
# on any non-copied files? Test this.
# Verify that the user wants to proceed:
ans = raw_input("Proceed? (y/n) ")
if ans and ans[0].lower() != 'y' :
print "\nNot syncing"
sys.exit(0)
print "Syncing ..."
child = pexpect.spawn(sync_cmd % (src, dst))
if passwd :
child.expect('password:')
child.sendline (passwd)
print "Sent password again"
#child.interact()
while child.isalive() :
print child.readline(),
child.close()
restore_all_atimes(src)
if __name__ == "__main__" :
if len(sys.argv) != 3 :
Usage()
src = sys.argv[1]
dst = sys.argv[2]
sync(src, dst, True)