3

I have a command stored in a variable, which I then run:

[root@cxpxwly01wel001 init.d]# cat test
#!/bin/bash
command="su - root -c 'java -Xms16m -the_rest_of_my_java_command'"
echo $command
$command
[root@cxpxwly01wel001 init.d]# ./test
su - root -c 'java -Xms16m -the_rest_of_my_java_command'
su: invalid option -- 'X'
Try `su --help' for more information.

The su error is what you get if the single quotes aren't there:

[root@cxpxwly01wel001 init.d]# su - root -c java -Xms16m -the_rest_of_my_java_command
su: invalid option -- 'X'
Try `su --help' for more information.

With the single quotes there the command works as expected:

[root@cxpxwly01wel001 init.d]# su - root -c 'java -Xms16m -the_rest_of_my_java_command'
-bash: java: command not found

Which suggests to me that when running the variable as a command the single quotes aren't preserved. How do I preserve them?

Note: I'm editing an existing script (inserting the su - around the java command), so I'd like to change as little as possible to keep it close to the source.


Let me add the output when run with set -x:

+ command='su - root -c '\''java -Xms16m -the_rest_of_my_java_command'\'''
+ echo su - root -c ''\''java' -Xms16m '-the_rest_of_my_java_command'\'''
su - root -c 'java -Xms16m -the_rest_of_my_java_command'
+ su - root -c ''\''java' -Xms16m '-the_rest_of_my_java_command'\'''
su: invalid option -- 'X'
Usage: su [options] [LOGIN]

Options:
  -c, --command COMMAND         pass COMMAND to the invoked shell
  -h, --help                    display this help message and exit
  -, -l, --login                make the shell a login shell
  -m, -p,
  --preserve-environment        do not reset environment variables, and
                                keep the same shell
  -s, --shell SHELL             use SHELL instead of the default in passwd
hek2mgl
  • 152,036
  • 28
  • 249
  • 266
mazz0
  • 683
  • 2
  • 8
  • 19

1 Answers1

1

The technique I use to avoid the "quoting inside quotes" issues is to build the command in an array. For instance, instead of :

command="su - root -c 'java -Xms16m -the_rest_of_my_java_command'"

I would use :

declare -a command=(su - root -c 'java -Xms16m -the_rest_of_my_java_command')

You can still echo the command (with an array expansion) :

echo "${command[@]}"

Or run it :

"${command[@]}"

The double quotes around the array expansion are important : they tell the shell to expand items without doing any word splitting, so an item containing whitespace will keep its identity as a single string.

By doing it this way, the last argument, which is inside quotes, will remain a single string, and the su command will not see -Xms16m as an (invalid) option.

As an aside, the [@] in "${command[@]}" means "all elements in the array". Whereas, in an expression like "${command[1]}", [1] would mean "only element at index 1".

Fred
  • 6,590
  • 9
  • 20
  • `++` for demonstrating how it should be done, also well explained here [BashFAQ-050](http://mywiki.wooledge.org/BashFAQ/050) – Inian Feb 09 '17 at 11:30
  • So it has to involve a change at the point where the script is run? I'm editing part of a much longer script, I really just want to change the command but not change the parts of the script that invoke it. Currently I'm inclined to make a second script which just calls the original script as the desired user. – mazz0 Feb 09 '17 at 15:58