Search code examples
bashrsync

Expanding a variable in Bash as a parameter to rsync


I tried to write a very simple synchronizing script that uses rsync with some params. I wanted these params to be stored in a variable in my script. Here is a minimal example:

$tree
.
├── a
│   └── aaa_p=1_k=1
│       └── x.txt
├── b
│   └── aaa_p=1_k=1
│       └── x.txt
└── script.sh

$cat script.sh

#!/bin/bash

PARAMS="--exclude '*p=*'"
rsync -av ${PARAMS} ./a/* ./b/

But when I run script.sh, it synchronizes x.txt, although x.txt it resides in a path containing p= that should be excluded. At the same time, when I run from command line: rsync -av --exclude '*p=*' ./a/* ./b/ then the exclude is respected, and synchronization does not happen.

What do I do badly in the script, so the exclude switch is not taken into account? Is it variable substitution in Bash?


Solution

  • When you type --exclude '*p=*' ./a/* ./b/ in your terminal the ' quotes are removed by bash. rsync sees only the string *p=*. But when you write PARAMS="--exclude '*p=*'"; rsync -av ${PARAMS} the quotes inside the variable stay, telling rsync to exlcude files that start and end with a '.

    You could leave out the ' quotes inside the variable, but the * would expand before rsync runs (this was also the case before, but '*p*' probably didn't match any file). You could quote "$PARAMS" to avoid expanding *, but then the whole string --exclude *p=* would be treated as one argument.

    The correct way to go about this is to use an array:

    params=(--exclude '*p=*')
    rsync -av "${params[@]}" ./a/* ./b/