1

I wrote a bash script with the following:

SRC="dist_serv:$HOME/www/"
DEST="$HOME/www/"
OPTIONS="--exclude 'file.php'"
rsync -Cavz --delete $OPTIONS $SRC $DEST

rsync fails and I can't figure out why, although it seems to be related to the $OPTIONS variable (it works when I remove it). I tried escaping the space with a backslash (among many other things) but that didn't work. The error message is :

rsync: mkdir "/home/xxx/~/public_html/" failed: No such file or directory (2)

I tried quoting the variable, which throws another error ("unknown option" on my variable $OPTIONS):

rsync: --exclude 'xxx': unknown option
rsync error: syntax or usage error (code 1) at main.c(1422) [client=3.0.6]
mrtnmgs
  • 1,402
  • 14
  • 26

2 Answers2

0

You shouldn't put $ in front of the variable names when assigning values to them. SRC is a variable, $SRC is the value that it expands to.

Additionally, ~ is not expanded to the path of your home directory when you put it in quotes. It is generally better to use $HOME in scripts as this variable behaves like a variable, which ~ doesn't do.

Always quote variable expansions:

rsync -Cavz --delete "$OPTIONS" "$SRC" "$DEST"

unless there is some reason not to (there very seldom is). The shell will perform word splitting on them otherwise.

User @Fred points out that you can't use double quotes around $OPTIONS (in in comments below), but it should be ok if you use OPTIONS='--exclude="file.php"' (note the =).

Kusalananda
  • 14,885
  • 3
  • 41
  • 52
  • The OPTIONS variable cannot be quoted, because it contains two words, and quoting will prevent them from being split when interpreting the command (they will be considered as a single string). In this case, not quoting OPTIONS would work because it does not contain any spaces, but it is not a general solution. – Fred Jan 16 '17 at 20:08
  • yes of course re: variable assignment! Editing my question... – mrtnmgs Jan 16 '17 at 21:16
  • 1
    @immicile If you could also mention the exact error message that you get from `rsync` in the question, that would help a lot! – Kusalananda Jan 16 '17 at 21:19
0

One technique which I find invaluable is using positional parameters to make it easy to work with list of options.

When you put options inside a variable (such as your OPTIONS variable), you need to find a way to include quotes inside the value, and omit quotes when referencing the variable. It works, but you are always one typo away from a difficult to debug failure.

Instead, try the following.

set -- -Cavz --delete
set -- "$@" --exclude "file.php"
set -- "$@" "dist_serv:~/www/"
set -- "$@" "~/www/"
rsync "$@"

Of course, in this case, everything could be on the same line, but in many cases there will be conditional expressions so that, for instance, you can omit a given option, or select difference files to work with. The nice thing is, you always use the same quoting you would use on a single command line, all thanks to the magic of "$@" that avoids having to reference (or quote) any specific variable.

If actual positional parameters get in the way, you can put them in variables, or create a function to isolate a context that avoids touching them where they matter.

I use this trick all the time, and I have stopped pulling my hair out due to quoting causing problems inside values I pass as parameter to commands.

A similar result can be achieved by using an array.

declare -a ARGUMENTS=()
ARGUMENTS=(-Cavz --delete )
ARGUMENTS+=(--exclude "file.php")
ARGUMENTS+=("dist_serv:~/www/")
ARGUMENTS+=("~/www/")
rsync "${ARGUMENTS[@]}"
Fred
  • 6,590
  • 9
  • 20
  • That's a nice workaround but my script already uses positional parameters and they would get overwritten. – mrtnmgs Jan 16 '17 at 21:50
  • I have added an array-based solution that produces similar results without using positional parameters. – Fred Jan 16 '17 at 22:03