Bash add value to array with embedded variable and single quotes

arraybashquoting

I'm sure it's completely clear from the subject 🙂 Joking aside, I think part of the problem I'm having finding an answer is writing the search terms.

I have had good luck when issuing commands in a bash script by using an array for the arguments.

For example, I'm calling rsync and created an array like (spolier alert – superfluous variables will be referenced later):

#!/usr/bin/env bash
set -euo pipefail
rf=.rsync-filter
srcff="/home/roger/home$rf"

hm=/home/roger
syncto=/tmp/to-remote/home

rsarg=(-avv --prune-empty-dirs --stats)
rsarg+=(--dry-run)

rsync "${rsarg[@]}" "$hm" "$syncto/"  #works

exit

Where I'm having problems is in adding the $srcff variable to a filter rule and adding that to the rsarg array.

rsarg+=("--filter='merge $srcff'") # no dice
# or this
rsarg+=("--filter='merge ${srcff}'") # sad trombone

Upon running I get:

$ ./home-sync-simple
Unknown filter rule: `'merge /home/roger/home.rsync-filter''
rsync error: syntax or usage error (code 1) at exclude.c(904) [client=3.1.1]

I can get it to work by adding the merge rule to the rsync line, outside of the array and minus embedded variable:

rsync "${rsarg[@]}" --filter='merge /home/roger/home.rsync-filter' "$hm" "$syncto/"

Replacing the above filter with the variable outside the array, fails in the same manner:

rsync "${rsarg[@]}" "--filter='merge $srcff'" "$hm" "$syncto/" 

I have tried many other variations, I'm assuming all possible excepting the one that actually works. Sometimes it results in "unexpected end of filter rule: merge" and other things. I've excluded those for brevity and because I think the versions above are closer to correct (he said, nervously). I am guessing I end up passing too few/many arguments.

Thanks in advance!

Best Answer

If this works:

rsync "${rsarg[@]}" --filter='merge /home/roger/home.rsync-filter' ...

Then I suppose you do not want to give the single-quotes to rsync. Here, the shell eats them before rsync sees them. Your other examples have quotes within quotes, so the inner ones stay and are passed for rsync to see.

So skip the single-quotes when building the array:

rsarg+=("--filter=merge $srcff")

Note that from the shell's point of view, the equal sign is nothing special, and there's no need to treat the part that comes after any differently than the part before. --foo=bar is the same as --foo="bar" or "--foo=bar", or even --fo"o=ba"r.

Related Question