2

This question already has an answer here:

From https://unix.stackexchange.com/a/32227/674

The main places where it's safe not to use the double quotes are:

  • in an assignment: foo=$bar (but note that you do need the double quotes in export "foo=$bar" or in array assignments like array=("$a" "$b"));
  1. Does it mean that it is not required to double quote $bar in assignment foo=$bar? Why?

  2. Why do we need the double quotes in export "foo=$bar" and in array assignments like array=("$a" "$b")?

Thanks.

marked as duplicate by Gilles bash Aug 3 '17 at 23:02

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

6
foo=$bar

is safe because it's an assignment, and an assignment to a scalar variable, using the scalar assignment syntax. It's a scalar context, only one value can be stored in $var, it would not make sense to split or glob $bar. If the expansion resulted in several words, the shell would need to somehow combine them again to be able to store them as one string in $foo.

It's different when you use:

foo=($bar)

Where you're assigning to an array variable. There it's a list context. You're assigning a number of words to elements of the array. split+glob occurs.

Also beware of the double-nature of things like export/local/typeset/declare/readonly in some shells (explained in more details at Are quotes needed for local variable assignment?)

You'll notice that:

foo=$bar

is parsed as an assignment while

"foo"=$bar

is just an attempt to run the foo=content_of_bar command (where the content of bar is subject to split+glob).

In shells where export (and other local/typeset...) is both a keyword and builtin (ksh, bash and recent versions of zsh), in:

export foo=$bar

export is recognised as a keyword and foo=$bar as an assignment, so $bar is not subject to split+glob. But it takes little for export to stop being recognised as a keyword. In which case, it's just treated as a simple command and split+glob happens like in any argument to any other command.

And even in the cases where export is seen as a keyword, if the arguments don't look like variable assignments (like in the "foo"=$bar above), then they're treated like normal arguments and subject to split+glob again.

  • Define "list context". – Arrow Aug 5 '17 at 0:06
  • Thanks. (1) "In shells where export (and other local/typeset...) is both a keyword and builtin (ksh, bash and recent versions of zsh)". I think export is only a builtin in bash? (2) "it takes little for export to stop being recognised as a keyword." How do you make it? How do you make that happen? – Tim Jun 8 '18 at 4:50
  • @Tim see the linked Q&A. – Stéphane Chazelas Jun 8 '18 at 6:36
3

To answer your two questions, we first have to know what "being safe" means. Bash has a multitude of expansions and they all happen in a certain order. Usually when we say something is "safe", we mean that no unwilling word splitting happens. In this sense:

  1. is "safe", i.e. no double quotes are required. To quote the bash manual:

    A variable may be assigned to by a statement of the form

              name=[value]
    

    If value is not given, the variable is assigned the null string. All values undergo tilde expansion, parameter and variable expansion, command substitution, arithmetic expansion, and quote removal (see EXPANSION below).

    Note that word splitting is not performed! So in that regard it is reasonably safe and executing bar="rm asdf"; foo=$bar; will not result in anything crazy, like failing to execute, or even your file being deleted, as $foo or eval $foo would. However, what will happen is variable expansion:

    foo="Tomatoes are $50" will result in foo having the content Tomatoes are 0. This may or may not be what you wanted.

  2. works in a similar way: foo="rm asdf"; export bar=$foo will not remove your file and will parse correctly, and foo="rm asdf"; array=($foo) will parse too. Note that in the case of the array, word splitting actually does happen, so the first element of array will be rm.

    Also, you will still get the other expansion in both of those. I'm also fairly certain word splitting happens in the export case, it's just that exports accepts all of the words as arguments and parses it in the way you expect it to, similar to how echo $foo doesn't need quotes.

Out of all of these, I would call the array example the least safe one, because word splitting does happen and has a concrete effect. Therefore you will need double quotes in this case.

  • No, in bar="*"; foo=$bar, both foo and bar contain *. It's only if you do echo $bar instead of echo "$bar" that the * is then expanded. – Stéphane Chazelas Aug 3 '17 at 20:01
  • @StéphaneChazelas You're completely right, I just noticed too. I became suspicious after I read further in the bash manual. Shows me how careful one should be with expansion, I guess. I walked right into it :) – PawkyPenguin Aug 3 '17 at 20:19

Not the answer you're looking for? Browse other questions tagged or ask your own question.