Triple nested quotations in shell script
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).
Degran
Updated on June 04, 2022Comments
-
Degran almost 2 years
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 runrsync $5 "$path"
where the destination
$path
is calculated from text inStamp
.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)
- https://unix.stackexchange.com/questions/23347/wrapping-a-command-that-includes-single-and-double-quotes-for-another-command
- how to make nested double quotes survive the bash interpreter?
- Using multiple layers of quotes in bash
- Nested quotes bash
I was unsuccessful in applying the solutions to my problem.
-
l0b0 over 10 yearsNit:
in "$@"
is redundant. -
Sir Athos over 10 years@l0b0: Please elaborate, is there a simpler syntax for it?
-
l0b0 over 10 yearsJust remove it.
for foo
will loop over"$@"
without any additional syntax. Syntactic sugar. -
Degran over 10 yearsThat 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 over 10 yearsThe "\ " 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 over 10 yearsI 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?
-
Sir Athos over 10 years@Degran: It would work, just run
shift 4; rsync "$@"
to pass all parameters from the 5th onward. -
SriniV over 8 years@SirAthos eval is evil (pun intended); try to avoid it