bash variables and rsync excludes
bash
and rsync
don’t necessarily work and play well together
when rsync’s --exclude
option is set as a variable.
Try this:
# on source host, create test directory and files
cd /var/tmp
mkdir mydir
pushd mydir
touch file1.txt file2.txt file3.pdf file4.pdf
popd
# get whole tree to remote host; note LACK of trailing
# slash on source directory
rsync -av /var/tmp/mydir remote.host:/var/tmp
At this point, localhost:/var/tmp/mydir
and remote.host:/var/tmp/mydir
are identical.
Excluding a file
I want to exclude file1.txt
from the rsync and delete it from the remote
host.
# on source host. note INCLUSION of trailing slash on source
# directory; we presume remote directory already exists.
rsync -av \
--exclude 'file1.txt' --delete-excluded \
/var/tmp/mydir/ remote.host:/var/tmp/mydir
file1.txt
no longer exists on the remote host. So far, so good!
Say, however, you want to pass the excluded file as a variable:
# reset remote directory so it's the same as the local source
rsync -av /var/tmp/mydir remote.host:/var/tmp
# set an exclude variable
EXCLUDE="--exclude 'file1.txt'"
rsync -av \
$EXCLUDE --delete-excluded \
/var/tmp/mydir/ remote.host:/var/tmp/mydir
# FAIL! file1.txt will still be on the remote host
Removing the single quote marks from the variable works, e.g,
EXCLUDE="--exclude file1.txt"
but then you can’t include file globs or wildcards in your exclude list.
I tried several variations that all failed:
# swap single and double quotes
EXCLUDE='--exclude "file1.txt"'
# escaped quotes
EXCLUDE="--exclude \"file1.txt\""
# all of the above with = sign in option, e.g.,
EXCLUDE="--exclude='file1.txt'"
An array of solution
The solution is to use a bash array, but I admit I do not understand why it works.
OPTLIST=(-a)
OPTLIST+=(-v)
OPTLIST+=(--delete-excluded)
for EXC in '*.txt' 'file4.pdf'; do
OPTLIST+=(--exclude "$EXC")
done
# note quotes around array variable; this always works. without quotes
# it will fail if you have wildcards in your exclude pattern(s)
rsync "${OPTLIST[@]}" /var/tmp/mydir/ remote.host:/var/tmp/mydir