Bash problem with eval, variables and quotes

Cesar picture Cesar · Mar 10, 2011 · Viewed 34.6k times · Source

I've been reading about quotes in bash here and everywhere else, but i got no help solving this problem.

The thing is, I have a little script for doing backups in a loop.

If I don't use eval then I have problems with $OPTIONS variable in rsync.

But if I do use eval then the problem goes to the variable $CURRENT_DIR...

rsync returns the following message: 'Unexpected local arg: /path/with'

I've tried every way of quoting the variable $CURRENT_DIR

CURRENT_DIR="/path/with spaces/backup"
DIR="dir_by_project"
f=":/home/project_in_server"
OPTIONS="-avr --exclude 'public_html/cms/cache/**' --exclude 'public_html/cms/components/libraries/cmslib/cache/**' --delete"
eval rsync --delete-excluded -i $OPTIONS  [email protected]$f $CURRENT_DIR/xxx/$DIR/files

Is there any way that i can use the variable $CURRENT_DIR without problems caused by spaces?

Answer

Erik picture Erik · Mar 10, 2011
eval rsync --delete-excluded -i $OPTIONS  [email protected]$f "\"$CURRENT_DIR/xxx/$DIR/files\""

command "some thing" executes command with one argument some thing. The quotes are parsed by the shell and arguments are setup as an array when executing the command. The command will see an argument as some thing without the quotes.

The eval command treats its arguments more or less as if they were typed into the shell. So, if you eval command "some thing", bash executes eval with two arguments: command and some thing (again the quotes are eaten while bash sets up the array of arguments). So, eval then acts as if you typed command some thing in the shell, which is not what you want.

What I did was simply to escape the quotes, so that bash passes literally "some thing" including the quotes to eval. eval then acts as if you typed command "some thing".

The outer quotes in my command is not strictly required, they're just habit. You could just as well use:

eval rsync --delete-excluded -i $OPTIONS  [email protected]$f \"$CURRENT_DIR/xxx/$DIR/files\"