#!/usr/bin/python3 # This script takes a command-line arg of a source directory # that will be passed to rsync, and generates a set of excludes # that will exclude all mount points from the list. This is # useful if you have "bind" mounts since the --one-file-system # option won't notice the transition to a different spot on # the same disk. For example: # # mnt-excl /dir | rsync --exclude-from=- ... /dir /dest/ # mnt-excl /dir/ | rsync --exclude-from=- ... /dir/ /dest/ # ssh host mnt-excl /dir | rsync --exclude-from=- ... host:/dir /dest/ # # Imagine that /dir/foo is a mount point: the first invocation of # mnt-excl would have output /dir/foo, while the second would have # output /foo (which are the properly anchored excludes). # # NOTE: This script expects /proc/mounts to exist, but could be # easily adapted to read /etc/mtab or similar. # # ADDENDUM: The addition of the --filter option (which has support for # absolute-anchored excludes) can make this script unneeded in some # scenarios. If you don't need delete protection on the receiving side # (or if the destination path is identical to the source path), then you # can exclude some absolute paths from the transfer based on the mount # dirs. For instance: # # awk '{print $2}' /proc/mounts | grep -v '^/$' | \ # rsync -avf 'merge,/- -' /dir host:/dest/ import os, argparse MNT_FILE = '/proc/mounts'; def main(): trailing_slash = '/' if args.path.endswith(('/', '/.')) and args.path != '/' else '' args.path = os.path.realpath(args.path) + trailing_slash parent_dir = os.path.dirname(args.path) trailing = os.path.basename(args.path) if not os.path.isdir(args.path): trailing = '' elif trailing != '': trailing += '/' want_path = os.path.join(parent_dir, trailing) wp_len = len(want_path) with open(MNT_FILE) as fh: for line in fh: mnt_path = line.split()[1] if mnt_path.startswith(want_path) and mnt_path != want_path: print(f"- /{trailing}{mnt_path[wp_len:]}") if __name__ == '__main__': parser = argparse.ArgumentParser(description="Output mount points as rsync excludes.", add_help=False) parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.") parser.add_argument('path', metavar='PATH', nargs='?', default='/', help="Limit output to those within the PATH hierarchy.") args = parser.parse_args() main() # vim: sw=4 et