Search code examples
bashrsync

Issues quoting exclusions for rsync in bash script


I'm using a bash script to sync a web folder using rsync. I'm using an array of folders and files to exclude, and I'm having problems escaping these items for the exclude list...

my exclude list is defined like so...

SYNC_EXCLUSIONS=(
    '/exclude_folder_1'
    '/exclude_folder_2'
    '.git*'
    '.svn'
)

Then I build my exclusion string like so...

exclusions='';
for e in "${SYNC_EXCLUSIONS[@]}"
do
    exclusions+=" --exclude='$e'";
done

Then finally I execute my rsync...

rsync --recursive --delete $exclusions "$DEPLOYMENT_WORK_DIR/" "$DEPLOYMENT_ROOT/"

If I echo the command it looks perfect, and if I copy and execute it at the prompt it works correctly. However when run from the script the exclusions are ignored.

I've figured out that it will work if I remove the single quotes from around each excluded item, like so...

exclusions+=" --exclude=$e";

I'd prefer to not do that though, just in case I need to exclude folders with spaces or special characters.

Is there some way I can get this to work from the script while retaining quotes around the excluded items? I've tried all sorts of combinations of quotes and backslashes etc. and nothing I've tried works.


Solution

  • You can't build a string for this at all -- see BashFAQ #50 for an extensive discussion of why. Build an array.

    exclusions=( )
    for e in "${SYNC_EXCLUSIONS[@]}"; do
        exclusions+=( --exclude="$e" )
    done
    
    rsync --recursive --delete "${exclusions[@]}" "$DEPLOYMENT_WORK_DIR/" "$DEPLOYMENT_ROOT/"
    

    ...well, can't build a string at all, unless you're going to execute it with eval. Doing that in a manner that isn't prone to shell injection vulnerabilities takes care, however:

    printf -v exclusions_str '--exclude=%q ' "${SYNC_EXCLUSIONS[@]}"
    printf -v rsync_cmd 'rsync --recursive --delete %s %q %q' \
      "$exclusions_str" "$DEPLOYMENT_WORK_DIR/" "$DEPLOYMENT_ROOT/"
    eval "$rsync_cmd"