Understanding rsync's include / exclude patterns

I want to transfer N files from a remote system, using a pattern:

ssh remote ls target/
2013-08-01.mjson.gz
2014-07-04.mjson.gz
2014-07-09.mjson.gz
2014-08-12.mjson.gz
...

I only want the 2014-07* files. My attempts so far:

rsync -a --dry-run --verbose --include="2014-07-*" remote:target .
# transfers everything

rsync -a --dry-run --verbose --include="2014-07-*" --exclude="*" remote:target .
# transfers nothing

rsync -a --dry-run --verbose --exclude="*" --include="2014-07-*" remote:target .
# transfers nothing

rsync -a --dry-run --verbose --include="*/2014-07-*" --exclude="*" remote:target .
# transfers nothing

rsync -a --dry-run --verbose --include="***/2014-07-*" --exclude="*" remote:target .
# transfers nothing

I know I can use --files-from to indicate which files exactly I want, but it's a pain to do.

I never understood how to correctly use include/exclude to do what I want. The man page states:

and the first matching pattern is acted on: if it is an exclude pattern, then that file is skipped; if it is an include pattern then that filename is not skipped; if no matching pattern is found, then the filename is not skipped.

Reading this, I presumed that if include was first, and the filename matched, then then "filename is not skipped" part would kick in. Apparently, I'm wrong.

Further down in the man page, I see:

Note that, when using the --recursive (-r) option (which is implied by -a), every subcomponent of every path is visited from the top down, so include/exclude patterns get applied recursively to each subcomponent’s full name

Since I used --recursive, did that cause . to be skipped, because of the global exclude?

How do I transfer a subset of files using include and exclude patterns?


Solution 1:

While you can use include and exclude, I think this would be a simpler solution:

rsync -a --dry-run --verbose remote:'target/2014-07*' .

The reason your include/exclude variant doesn't work is that rsync never descends into the target directory since it is excluded by the * pattern and not matched by the include pattern (which only matches files in target, not target itself). For include/exclude to work you would have to do something like

rsync -a --dry-run --verbose --include=target --include='target/2014-07-*' --exclude='*' remote:target .