4

I'm trying to write a shell script that calls another script that then executes a rsync command. The second script should run in its own terminal, so I use a gnome-terminal -e "..." command. One of the parameters of this script is a string containing the parameters that should be given to rsync. I put those into single quotes. Up until here, everything worked fine until one of the rsync parameters was a directory path that contained a space. I tried numerous combinations of ',",\",\' but the script either doesn't run at all or only the first part of the path is taken.

Here's a slightly modified version of the code I'm using

gnome-terminal -t 'Rsync scheduled backup' -e "nice -10 /Scripts/BackupScript/Backup.sh 0 0 '/Scripts/BackupScript/Stamp' '/Scripts/BackupScript/test' '--dry-run -g -o -p -t -R -u --inplace --delete -r -l '\''/media/MyAndroid/Internal storage'\''' "

Within Backup.sh this command is run

rsync $5 "$path"

where the destination $path is calculated from text in Stamp.

How can I achieve these three levels of nested quotations?

These are some question I looked at just now (I've tried other sources earlier as well)

I was unsuccessful in applying the solutions to my problem.

Community
  • 1
  • 1
Degran
  • 43
  • 1
  • 1
  • 3

2 Answers2

5

Here is an example. caller.sh uses gnome-terminal to execute foo.sh, which in turn prints all the arguments and then calls rsync with the first argument.

caller.sh:

#!/bin/bash
gnome-terminal -t "TEST" -e "./foo.sh 'long path' arg2 arg3"

foo.sh:

#!/bin/bash
echo $# arguments
for i; do    # same as: for i in "$@"; do
    echo "$i"
done
rsync "$1" "some other path"

Edit: If $1 contains several parameters to rsync, some of which are long paths, the above won't work, since bash either passes "$1" as one parameter, or $1 as multiple parameters, splitting it without regard to contained quotes.

There is (at least) one workaround, you can trick bash as follows:

caller2.sh:

#!/bin/bash
gnome-terminal -t "TEST" -e "./foo.sh '--option1 --option2 \"long path\"' arg2 arg3"

foo2.sh:

#!/bin/bash
rsync_command="rsync $1"
eval "$rsync_command"

This will do the equivalent of typing rsync --option1 --option2 "long path" on the command line.

WARNING: This hack introduces a security vulnerability, $1 can be crafted to execute multiple commands if the user has any influence whatsoever over the string content (e.g. '--option1 --option2 \"long path\"; echo YOU HAVE BEEN OWNED' will run rsync and then execute the echo command).

Sir Athos
  • 9,403
  • 2
  • 22
  • 23
  • Nit: `in "$@"` is redundant. – l0b0 Jan 16 '14 at 19:56
  • @l0b0: Please elaborate, is there a simpler syntax for it? – Sir Athos Jan 16 '14 at 21:00
  • 2
    Just remove it. `for foo` will loop over `"$@"` without any additional syntax. Syntactic sugar. – l0b0 Jan 16 '14 at 21:04
  • That is indeed a pretty similar and easier to read program. My problem now is that 1. `'long path'` not only contains the source, but also other parameters. So I would use `$1` instead of `"$1"` 2. The source is a string with a space, so it needs to be wrapped in quotations, which brings us to the triple nesting. I could separate the source from other parameters and let foo.sh have 4 arguments, but that wouldn't fix the problem with excludes, for example. – Degran Jan 16 '14 at 22:06
  • I did know of eval as a possibility but I'd rather not resort to using it. Instead, I was thinking of getting rid of the second level of (single) quotes around all the rsync parameters and simply giving rsync all the parameters from the fifth one onward as an array or something. I could then wrap the long path in single quotes and use something similar to your first method. Would this work? – Degran Jan 17 '14 at 21:11
  • @Degran: It would work, just run `shift 4; rsync "$@"` to pass all parameters from the 5th onward. – Sir Athos Jan 17 '14 at 21:41
  • @SirAthos eval is evil (pun intended); try to avoid it – Srini V Oct 15 '15 at 07:42
0

Did you try escaping the space in the path with "\ " (no quotes)?

gnome-terminal -t 'Rsync scheduled backup' -e "nice -10 /Scripts/BackupScript/Backup.sh 0 0 '/Scripts/BackupScript/Stamp' '/Scripts/BackupScript/test' '--dry-run -g -o -p -t -R -u --inplace --delete -r -l ''/media/MyAndroid/Internal\ storage''' "

JimR
  • 15,513
  • 2
  • 20
  • 26
  • The "\ " isn't treated as an escape character, probably for the same reason the quotes aren't regarded either when passing the parameters to rsync. – Degran Jan 17 '14 at 21:02