Triple nested quotations in shell script

11,017

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).

Share:
11,017
Degran
Author by

Degran

Updated on June 04, 2022

Comments

  • Degran
    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 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.

  • l0b0
    l0b0 over 10 years
    Nit: in "$@" is redundant.
  • Sir Athos
    Sir Athos over 10 years
    @l0b0: Please elaborate, is there a simpler syntax for it?
  • l0b0
    l0b0 over 10 years
    Just remove it. for foo will loop over "$@" without any additional syntax. Syntactic sugar.
  • Degran
    Degran over 10 years
    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
    Degran over 10 years
    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
    Degran over 10 years
    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?
  • Sir Athos
    Sir Athos over 10 years
    @Degran: It would work, just run shift 4; rsync "$@" to pass all parameters from the 5th onward.
  • SriniV
    SriniV over 8 years
    @SirAthos eval is evil (pun intended); try to avoid it