7

I'm trying to make a script which needs to save a command to be run as a string. The string in question needs to contain quotes, and when attempting to execute it bash adds additional quotation characters which in some cases causes the command to not run as expected.

Here is an example script. The command example run in this example script obviously doesn't do anything and it will simply say command not found, however you can still run the script and see what I'm talking about because I added -x to the #!/bin/bash line so that the exact command being run is printed out.

Example:

#!/bin/bash -x

#command typed explicitly
example -options "-i filename"

#command stored in string first
commandstring='example -options "-i filename"'
echo "running command: ${commandstring}"
${commandstring}

The output of running this script for me is (ignoring the two "command not found" errors):

+ example -options '-i filename'
+ commandstring='example -options "-i filename"'
+ echo 'running command: example -options "-i filename"'
running command: example -options "-i filename"
+ example -options '"-i' 'filename"'

So you can see that the first line of course is run as expected giving the command line parameter:

-i filename

However, although the echo command prints the string in a way that would execute perfectly as well, when the string is placed on the command line it changes and the parameter being passed in becomes

'"-i' 'filename"'

which contains additional quote characters, and this is causing the actual command I'm using in my real script to fail.

Is there any way to prevent this?

HalosGhost
  • 4,790
stss
  • 83

1 Answers1

19

If you are using bash, the easiest way to save a command exactly is to put it in an array. If the command you want to run is:

example -options "-i filename"

you can save it in an array variable with:

commandline=(example -options "-i filename")

Then, you can run the saved command exactly as is by @-expanding the array inside double-quotes:

"${commandline[@]}"

The key thing to know here is that the shell treats quote characters ("'\) differently when typed directly into the command line from when it finds them inside a variable expansion like in your ${commandstring}. In the command-line, quote characters have a special quoting function. Inside a parameter expansion, they are just normal non-special characters. Thus, "-i filename" is a single word when typed in the command line that gets parsed as -i filename, but the same characters in a variable expand to the words "-i and filename".


BashFAQ 50 covers this topic in far greater detail.

jw013
  • 51,212
  • That fixed it, and thanks for the awesome explanation – stss Oct 10 '14 at 03:13
  • echo "${commandline[@]}" prints the line without quotes, as does echo ${commandline[@]}. Is there a way to also print the array with the quotes intact? – Donn Lee Dec 16 '19 at 21:04
  • ok, to also output ("echo") the command, I ended up using this hack: Put set -x before the command and set +x after the command. – Donn Lee Dec 16 '19 at 21:14