2

Could someone please explain to me this behaviour I noticed lately when I was sync'ing a directory to another server via rsync? I wanted to exclude the sub-directory "done" in this case.

When I put the options to use with rsync into a variable, that directory was not excluded. But it was when I put the options directly behind the rsync call. Changes to "-av" made a difference, but the exclude didn't work.

rsync 3.0.9-18, on bash, CentOS 7.4

Not excluded:

$ RSYNC_OPTIONS='-av --exclude "done"'
$ touch done/test.ignore && rsync ${RSYNC_OPTIONS} ${SOURCEDIR} ${TARGET}
sending incremental file list
done/test.ignore

sent 132 bytes  received 32 bytes  109.33 bytes/sec
total size is 0  speedup is 0.00

Excluded:

$ touch done/test.ignore && rsync -av --exclude "done" ${SOURCEDIR} ${TARGET}
sending incremental file list

sent 41 bytes  received 12 bytes  106.00 bytes/sec
total size is 0  speedup is 0.00
2

2 Answers 2

2

I have replicated the problem and used set -x to see what the both commands really look like. It turned out this command

rsync ${RSYNC_OPTIONS} ${SOURCEDIR} ${TARGET}

is in fact equivalent to this

rsync -av --exclude '"done"' ${SOURCEDIR} ${TARGET}

Note the quotes in quotes. Your pattern is not done; it's "done", as if the directory you want to exclude had double quotes in its actual name.


To almost fix this you can declare the variable without these troublesome quotes:

RSYNC_OPTIONS='-av --exclude done'                           # poor fix, don't
rsync ${RSYNC_OPTIONS} "${SOURCEDIR}" "${TARGET}"

But this will backfire if the pattern contains spaces etc. Another approach may be with eval:

RSYNC_OPTIONS='-av --exclude "name  with  double  spaces"'
eval rsync "${RSYNC_OPTIONS}" '"${SOURCEDIR}" "${TARGET}"'   # not recommended

eval will parse the line for the second time. It's very hard to use it right and safely. I double quoted ${RSYNC_OPTIONS} so "name with double spaces" doesn't lose double spaces. I single quoted "${SOURCEDIR}" "${TARGET}", so these variables are not expanded right away (otherwise their content would undergo expansion). This is tricky!

Besides, what about name "with' quotes? To get this exact string as an option-argument to rsync --exclude you need some obscure quoting and escaping in RSYNC_OPTIONS declaration. There are more reasons to avoid eval.


The real solution is to use an array in Bash. Note arrays are not portable.

RSYNC_OPTIONS=(-av --exclude 'name with  spaces and $u(h')
rsync "${RSYNC_OPTIONS[@]}" "${SOURCEDIR}" "${TARGET}"

I understand why you didn't quote ${RSYNC_OPTIONS} in your original approach. You should have quoted ${SOURCEDIR} and (separately) ${TARGET} though. The above command quotes each variable properly.

Or maybe ${SOURCEDIR} was meant to specify multiple sources? This would be the reason not to quote it, but then it could bring similar issues as ${RSYNC_OPTIONS}. In this case you should definitely use an array variable here as well.

Also consider variable names in lower case.

1
  • Thanks very much for this thorough explanation! Haven't put the other variables in quotes as those really are constants (I exactly know the one value they contain). Will have a look at the "lower case" link.
    – Larsen
    Commented Jan 23, 2019 at 17:10
-1

What about placing your quotes like this ?

RSYNC_OPTIONS='-av --exclude '"done"''

I only tested it on a simplified command:

SEP='-F ";"'
echo "1;2;3" | awk ${SEP} '{print $2}'

(not working)

SEP='-F '";"''
echo "1;2;3" | awk ${SEP} '{print $2}'

(working)


Edit:

With @Kamil technique, array works well too:

SEP=(-F ";")
echo "1;2;3" | awk ${SEP[@]} '{print $2}'
4
  • 2
    A red herring. SEP='-F '";"'' defines the same variable value as SEP='-F ;' (compare declare -p SEP in both cases), therefore your working example is just my "poor fix" in disguise. I downvoted because this answer brings nothing new, except this weird quoting technique that only obfuscates the problem. Commented Jan 23, 2019 at 15:07
  • @KamilMaciorowski I'm a bash newbie, I just like to think there's a workaround with using only strings, but obviously I'm wrong.
    – Yoric
    Commented Jan 23, 2019 at 15:27
  • Well, in fact there is a workaround. It involves eval and it's rather awful. My answer is now updated to cover this. While I think your answer here is not useful, there are two other answers of yours I upvoted today. In my opinion there's a mishap here, but in general you're doing well. Commented Jan 23, 2019 at 16:25
  • @KamilMaciorowski Thanks for adding to your already clear explanation, and for your concern. We do learn a lot from experienced techies like you that are kind enough to take the time to even put the appropriate links to read further. I am ok to be wrong as long as I learn more :) I am just amazed with the Linux World and a bit frustrated not to know 5% of it.
    – Yoric
    Commented Jan 23, 2019 at 16:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .