Why do bash parameter expansions cause an rsync command to operate differently?

The shell parses quotes before expanding variables, so putting quotes in a variable’s value doesn’t do what you expect — by the time they’re in place, it’s too late for them to do anything useful. See BashFAQ #50: I’m trying to put a command in a variable, but the complex cases always fail! for more details.

In your case, it looks like the easiest way around this problem is to use an array rather than a plain text variable. This way, the quotes get parsed when the array is created, each “word” gets stored as a separate array element, and if you reference the variable properly (with double-quotes and [@]), the array elements get included in the command’s argument list without any unwanted parsing:

filter=(--include="lib/***" --include="arm-none-eabi/include/***" \
  --include="arm-none-eabi/lib/***" --include="*/" --exclude="*")
rsync -amnv "${filter[@]}" /tmp/from/ /tmp/to/

Note that arrays are available in bash and zsh, but not all other POSIX-compatible shells. Also, I lowercased the filter variable name — recommended practice to avoid colliding with the shell’s special variables (which are all uppercase).

Leave a Comment