rsync a selection of directories to destination and delete anything else there

I have read the rsync docs and looked through threads here but cannot find a simple match for my requirement.

I wish to copy a number of directories/files from source to a destination directory. I want the incremental copying, but I want anything else removed from the destination. So in the destination we end up with an exact backup of the sources specified, and nothing else.

#!/bin/bash
mkdir -p source/{A,B,C,D}
touch source/{A,B,C,D}/file
mkdir -p /tmp/dest/{A,B,E,F}
touch /tmp/dest/{A,B,E,F}/file
cd source
rsync -av --delete A B /tmp/dest/

I want /tmp/dest to end up with nothing but A and B, so E and F should get deleted — but they do not. Note that I do not wish to copy C and D from the source. Note that I do not know what else might or might not be at the destination (nor at the source).

This seems like a common enough requirement for backing up some selective stuff and having the destination contain nothing but what was requested. Anybody have the exact command for this, please?

Asked By: JonBrave

||

You should be syncing source with dest while using inclusion and exclusion filters to specify exactly what you want.

When you sync A and B from within source to dest, you essentially sync source/A with dest/A and source/B with dest/B. The utility has no knowledge of any other directories under source or dest or how they should be handled.

Instead:

rsync -av --delete --delete-excluded 
    --include='A/***' 
    --include='B/***' 
    --exclude='*' 
    source/ /tmp/dest/

This asks rsync to make source and dest identical within the constraints given by the various filters. The / at the end of source/ is essential (without it, you’d get /tmp/dest/source as a directory), while the / at the end of the destination path is to ensure it’s actually a directory. The filters are applied to each name under source in a "first match wins" manner. The two inclusion filters will match the A and B subdirectories and all of their contents (recursively). The exclusion filter will match anything else.

We run with --delete and --delete-excluded which will both delete files that should not exist under dest on the destination (e.g., E and F or any deleted file under A or B), and it will delete anything matching an exclusion filter (e.g., C and D if they existed under dest).


From a comment about filter expressions:

In an inclusion or exclusion filter, * matches any name in a directory, ** matches any name in a directory or below it, and *** matches any name in a directory or below it but also the directory itself, i.e, A/*** and B/*** matches A and B, not just their contents. If we were to use A/** and B/**, these directories would never be synced as the pattern A/** does not match A and B/** does not match B.

This is described in the rsync(1) manual.

Answered By: Kusalananda
Categories: Answers Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.