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.