diff --git a/README.md b/README.md index 6b517dc..9408907 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,7 @@ Synchronize the contents of two directories. The directory can either be local o * -f/--force: override existing file instead of showing error message. * -n/--dry-run: emulate the operation without real sync. * --delete-removed: delete files not in source directory. +* --exclude: exclude files with the given pattern(s). Only works for local files to be uploaded to s3 directory. This option can be provided several times. #### `s4cmd sync [source] [target]` @@ -263,6 +264,9 @@ before given parameter. Condition on files where their last modified dates are after given parameter. +##### `--exclude` +(local) Filenames, matching the given pattern (https://docs.python.org/3.4/library/fnmatch.html) will not be synced to s3 directory. You may provide this param several times. + ## S3 API Pass-through Options diff --git a/s4cmd.py b/s4cmd.py index b12a57b..864e7ca 100755 --- a/s4cmd.py +++ b/s4cmd.py @@ -994,8 +994,14 @@ def dsync_files(self, source, target): for src, dest in sync_list: pool.download(src, dest) elif not src_s3_url and dst_s3_url: + expats = [] + if self.opt.exclude: + expats = self.opt.exclude + info("excluded patterns: %s", expats) + for src, dest in sync_list: - pool.upload(src, dest) + if not any(fnmatch.fnmatch(src, expat) for expat in expats): + pool.upload(src, dest) elif src_s3_url and dst_s3_url: for src, dest in sync_list: pool.copy(src, dest) @@ -1815,6 +1821,18 @@ def check_dict(self, opt, value): TYPE_CHECKER['datetime'] = check_datetime TYPE_CHECKER['dict'] = check_dict + # enable multiple option values (e.g. --exclude '*.thumbnail.jpg' --exclude '*.layout.jpg') + ACTIONS = optparse.Option.ACTIONS + ("extend",) + STORE_ACTIONS = optparse.Option.STORE_ACTIONS + ("extend",) + TYPED_ACTIONS = optparse.Option.TYPED_ACTIONS + ("extend",) + ALWAYS_TYPED_ACTIONS = optparse.Option.ALWAYS_TYPED_ACTIONS + ("extend",) + + def take_action(self, action, dest, opt, value, values, parser): + if action == "extend": + values.ensure_value(dest, []).append(value) + else: + optparse.Option.take_action(self, action, dest, opt, value, values, parser) + def main(): try: if not sys.argv[0]: sys.argv[0] = '' # Workaround for running with optparse from egg @@ -1910,6 +1928,9 @@ def main(): '--last-modified-after', help='Condition on files where their last modified dates are after given parameter.', type='datetime', default=None) + parser.add_option( + '--exclude', help='Exclude all files from src dsync (file-)list matching the specified pattern', + dest='exclude', action="extend", type='string', default=None) # Extra S3 API arguments BotoClient.add_options(parser)