mazz0
mazz0

Reputation: 715

How do you stop bash from stripping quotes when running a variable as a command?

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

Upvotes: 3

Views: 455

Answers (1)

Fred
Fred

Reputation: 6995

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

Upvotes: 1

Related Questions