File: PC-Phone USB Sync/restore-symlinks-from-backup.py
#!/usr/bin/env python3 """ ====================================================================================== restore-symlinks-from-backup.py - a PC-Phone USB Sync app utility script. This script has the same terms of use as that of the app. It's available online at quixotely.com/PC-Phone USB Sync/restore-symlinks-from-backup.py. Run immediately after a SYNC, with a command line like this: python3 restore-symlinks-from-backup.py <TO-path> Where the required <TO-Path> argument is the relative or absolute pathname of the content folder which was TO in the preceding SYNC. This can be run in Terminal on Linux, and Command Prompt, Power Shell, and WSL on Windows. You must have a Python 3.x installed on the device where you run this. This script restores all symlinks (only) in TO that were removed or changed by the preceding SYNC, and were saved by the SYNC in TO's __bkp__ backups folder. Use this to restore symlinks on Linux or Windows which were deleted because they did not exist on the removable drive which was FROM in the SYNC. macOS doesn't require this because it forges symlinks on removable drives, so they will generally survive roundtrips to other platforms. Android doesn't require this because it doesn't support symlinks in usable folders. This script suffices in this specific usage context, but in general, you're better off avoiding symlinks altogether in content folders propagated to removable drives and devices that don't support them. See example ahead. Caution: this works only after a SYNC that removes symlinks. It will restore symlinks in TO whether they were deleted by omission in FROM, or changed by differences in FROM. Use this only if you're sure that the SYNC removed symlinks in TO because they were absent on a FROM removable drive (or other). More details: https://quicotely.com/PC-Phone USB Sync/Tech-Notes.html#n1 See also: https://learning-python.com/android-deltas-sync/_etc/find-all-symlinks.py ====================================================================================== """ import sys, os, glob, shutil LISTONLY = False # True = list backed-up symlinks without restoring them def exit(msg, label=True): print('Error: ' + msg if label else msg) sys.exit(1) if len(sys.argv) != 2: exit('Usage: python3 <TO-path>', label=False) TO = sys.argv[1] if not os.path.isdir(TO): exit('TO path is not a folder') TObkp = TO + os.sep + '__bkp__' if not os.path.isdir(TObkp): exit('TO path has no __bkp__ folder') bkppatt = 'date??????-time??????' # ignore '*--UNDONE', '__undoable__' backups = glob.glob(os.path.join(TObkp, bkppatt)) # mergeall prunes use 'date*-time*' if not backups: exit('TO path has no usable backups') latestbackup = sorted(backups)[-1] # latest by date/time folder name numlinks = 0 for (dirhere, subshere, fileshere) in os.walk(latestbackup): for filename in fileshere + subshere: # links in both lists bkppath = os.path.join(dirhere, filename) if os.path.islink(bkppath): numlinks += 1 if LISTONLY: print('Found:', bkppath) continue # map TO/__bkp__/date-time/path to TO/path bkpprefix = len(latestbackup) + len(os.sep) TOpath = os.path.join(TO, bkppath[bkpprefix:]) # partly yoinked from Mergeall's cpall.copylink() [tbd: FWP()?] # windows dir-link arg if sys.platform.startswith('win') and os.path.isdir(bkppath): dirarg = dict(target_is_directory=True) else: dirarg ={} # remove current link if present # lexists: link, not its target if os.path.lexists(TOpath): # else os.symlink() will fail os.remove(TOpath) # copy linkpath over linkPath = os.readlink(bkppath) # the from link's pathname str os.symlink(linkPath, TOpath, **dirarg) # store pathname as new link shutil.copystat(bkppath, TOpath, follow_symlinks=False) # report print('Restored: [%s] => [%s]' % (bkppath, TOpath)) print('Done: %s backed-up symlinks found %s.' % (numlinks, ('(only)' if LISTONLY else 'and restored'))) """ ====================================================================================== Example output: % python3 restore-symlinks-from-backup.py test-restore Restored: [/Users/me/Desktop/test-restore/__bkp__/date010203-time010203/link-file.txt] => [/Users/me/Desktop/test-restore/link-file.txt] Restored: [/Users/me/Desktop/test-restore/__bkp__/date010203-time010203/link-folder-file.txt] => [/Users/me/Desktop/test-restore/link-folder-file.txt] Restored: [/Users/me/Desktop/test-restore/__bkp__/date010203-time010203/link-folder.txt] => [/Users/me/Desktop/test-restore/link-folder.txt] Restored: [/Users/me/Desktop/test-restore/__bkp__/date010203-time010203/folder/link-parent-file.txt] => [/Users/me/Desktop/test-restore/folder/link-parent-file.txt] Restored: [/Users/me/Desktop/test-restore/__bkp__/date010203-time010203/folder/link-file.txt] => [/Users/me/Desktop/test-restore/folder/link-file.txt] Restored: [/Users/me/Desktop/test-restore/__bkp__/date010203-time010203/folder/link-parent.txt] => [/Users/me/Desktop/test-restore/folder/link-parent.txt] Done: 6 backed-up symlinks found and restored. ====================================================================================== """