78

Failing, simplified example:

FLAGS='--archive --exclude="foo bar.txt"'
rsync $FLAGS dir1 dir2

I need to include the quotes as if the command was like this:

rsync --archive --exclude="foo bar.txt" dir1 dir2
1

6 Answers 6

91

Short answer: see BashFAQ #50 ("I'm trying to put a command in a variable, but the complex cases always fail!").

Long answer: Putting commands (or parts of commands) into variables and then getting them back out intact is complicated. When the shell expands a variable on the command line, if the variable was in double-quotes it's not parsed; if it was not in quotes, spaces in it are parsed as argument breaks, but quotes and escape are not parsed. In either case, putting quotes in the variable's value does nothing useful.

Usually, the best way to do this sort of thing is using an array instead of a simple text variable:

FLAGS=(--archive --exclude="foo bar.txt")
rsync "${FLAGS[@]}" dir1 dir2
2
  • Thanks! Anything without intermediate variable (and not modifiying the command itself like with eval prefix) is impossible, right? Commented Nov 15, 2020 at 23:18
  • 2
    @mvorisek If I understand what you mean (using a non-array variable, and no eval or equivalent), then it's impossible. Commented Nov 16, 2020 at 5:17
8

eval is another option:

$ x="'a b'"
$ printf '%s,%s' $x
'a,b'
$ eval printf '%s,%s' $x
a b

See also:

4

Alternatively, you can create an alias for your command:

alias myrsync='rsync --archive --exclude="foo bar.txt"'
myrsync dir1 dir2
1
  • alias myrsync="rsync $FLAGS" is more correct answer to the specific question IMO Commented Dec 21, 2022 at 3:59
1

One option is to use eval to parse into an array. Obviously this will let whoever controls the string doing whatever they want.

FLAGS='--archive --exclude="foo bar.txt"'
eval "FLAGS=($FLAGS)"
rsync "${FLAGS[@]}" dir1 dir2
1
  • Thank-you. A variation of this answer using eval is what solved a similar problem for me. Commented Jan 19, 2025 at 15:13
0

As stated by @cbix, using an alias is a nice solution in this case.

Don't forget to allow aliases expansion in scripts by using the line

shopt -s expand_aliases

As explained here https://www.thegeekdiary.com/how-to-make-alias-command-work-in-bash-script-or-bashrc-file/ it is stated in the man page of bash:

Aliases are not expanded when the shell is not interactive, unless the expand_aliases shell option is set using shopt

-3

I don't see the problem :

$ FLAGS='--archive --exclude="foo bar.txt"'
$ echo $FLAGS
--archive --exclude="foo bar.txt"

Maybe you need to quote again the value :

$ rsync "$FLAGS" dir1 dir2
2
  • 5
    echo isn't showing what you think it is. Try printargs() { printf "'%s' " "$@"; echo; }; printargs $FLAGS; printargs "$FLAGS" to see why neither of these options work. Commented Nov 25, 2011 at 2:25
  • @GordonDavisson But printargs() prints same output if no arguments or if one empty string argument. Commented Dec 17, 2019 at 13:51

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.