14
> echo "hi"
hi
> VAR='echo "hi"'
> $VAR
"hi"

Why is the output of the above commands different?

A similar thing occurs with single quotes:

> VAR="echo 'hi'"
> $VAR
> 'hi'
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
Cory Klein
  • 18,391
  • 26
  • 81
  • 93
  • 6
    Please do not get in the habit of embedding executable script snippets in variables. This tends to be [tricky at best](http://mywiki.wooledge.org/BashFAQ/050) and [`eval` is a minefield of potential security holes which you must tread very carefully](http://mywiki.wooledge.org/BashFAQ/048) – jw013 May 11 '12 at 17:15
  • @jw013 Good point and great articles. I like the quote "Variables hold data, functions hold code." from the first link, but for my usage, the data that is given to a function (in this case, `at`) *is* code. Any tips on a safer way to organize/collect code that will be given to `at`? – Cory Klein May 11 '12 at 17:38
  • `at` takes `sh` syntax as input. Thus generating input for `at` means generating valid, properly quoted `sh` syntax from arbitrary input, which is not trivial, so I'd try to avoid it if at all possible. It would really help if you could give a little more detail on what you are trying to accomplish. – jw013 May 11 '12 at 17:56
  • Sorry, I didn't want to distract with too much detail, but what I'm doing isn't really complicated, IMO. I'm creating a script that takes a "time" and a "message". It then runs `at` for the given "time", and tells `at` to run the command `dzen2`. `dzen2` takes the "message" from stdin, and also uses some other static parameters. The difficulty is that I need to pipe the "message" parameter from the user into the `dzen2` command, but I'm not actually running `dzen2` myself, I'm telling `at` to do it. – Cory Klein May 11 '12 at 18:02
  • @jw013 I ended up getting everything to work just fine, by the way. – Cory Klein May 11 '12 at 18:33
  • "Variables hold data, functions hold code." Unless it's a lisp. – Joseph Kern May 12 '12 at 16:50
  • 1
    http://superuser.com/questions/360966/how-do-i-use-a-bash-variable-string-containing-quotes-in-a-command/936487#936487 || http://stackoverflow.com/questions/7454526/bash-variable-containing-multiple-args-with-quotes – Ciro Santilli OurBigBook.com Jul 05 '15 at 08:27

3 Answers3

18

The extra pair of quotes would be consumed only by an extra evaluation step. For example forced by eval:

bash-4.2$ VAR='echo "hi"'

bash-4.2$ $VAR
"hi"

bash-4.2$ eval $VAR
hi

But generally is a bad idea to put commands with parameters in one string. Use an array instead:

bash-4.2$ VAR=(echo "hi")

bash-4.2$ "${VAR[@]}"
hi
manatwork
  • 30,549
  • 7
  • 101
  • 91
  • 1
    It's also important to note that quotes are evaluated differently; double quotes (") allow evaluation of the enclosed string, single quotes (') print the string as a literal. Example: `"$(ls)"` and `'$(ls)'`. This is the reason why quotes appear in original questions examples. – Joseph Kern May 12 '12 at 17:00
  • An array is also a source of problems. Code belongs to functions, data to variables. The example you present works only because the quotes are removed in the split of the array. A `printf '<%s> ' "${VAR[@]}"` will show that quotes have been removed already. If you set VAR as `VAR=(echo \"hi\")` to actually have quotes, the same problem appears again, `$ ${VAR[@]}` will print `"hi"` –  Jan 17 '16 at 06:10
10

Quote removal only occurs on the original input words, not on the result of expansions. Quotes that are part of expanded variables are untouched.

jw013
  • 50,274
  • 9
  • 137
  • 141
3

If you step back a bit, you can see why variable substitution absolutely should retain quotes.

The point of quotes in a Unix/Linux/BSD shell is to keep pieces of a string together that would otherwise get parsed as multiple strings. Since by default a shell uses whitespace as a token separator, a string with spaces (like "one two three") if not quoted or escaped somehow, would get parsed as 3 strings: "one", "two" and "three".

If a programmer wants a string with the value of some variable interpolated:

VAR=two
STRING="one $VAR three"

the shell should absolutely not remove the quotes: the string containing spaces would get parsed as 3 smaller strings.