Are quotes needed for local variable assignment?
Quotes are needed in export foo="$var"
or local foo="$var"
(or readonly
, typeset
, declare
and other variable declaring commands) in:
dash
versions 0.3.8-15 to 0.5.10.2 (see change).- the
sh
of NetBSD (also based on the Almquist shell). - The
sh
of FreeBSD 9.2 or older (see the change in 9.3) yash
zsh
with versions prior to 5.1 inksh
orsh
emulation (or forexport var="$(cmd)"
wherezsh
would perform word splitting otherwise (not globbing)).
As otherwise the variable expansion would be subject to word splitting and/or filename generation like in any argument to any other command.
And are not needed in:
bash
ksh
(all implementations)- the
sh
of FreeBSD 9.3 or newer - busybox' ash-based
sh
(since 2005) zsh
dash
0.5.11 or newer.
In zsh
, split+glob is never done upon parameter expansion, unless in sh
or ksh
emulation, but split (not glob) is done upon command substitution. Since version 5.1, export
/local
and other declaration commands have become dual keyword/builtin commands like in the other shells above, which means quoting is not necessary, even in sh
/ksh
emulation and even for command substitution.
There are special cases where quoting is needed even in those shells though like:
a="b=some value"
export "$a"
Or more generally, if anything left of the =
(including the =
) is quoted or the result of some expansion (like export 'foo'="$var"
, export foo\="$var"
or export foo$((n+=1))="$var"
(that $((...))
should also be quoted actually)...). Or in other words when the argument to export
wouldn't be a valid variable assignment if written without the export
.
If the export
/local
command name itself is quoted (even in part like "export" a="$b"
, 'ex'port a="$b"
, \export a="$b"
, or even ""export a="$b"
), the quotes around $b
are needed except in AT&T ksh
, mksh
and recent versions of dash
.
If export
/local
or some part of it is the result of some expansion (like in cmd=export; "$cmd" a="$b"
or even export$(:) a="$b"
) or in things like dryrun=; $dryrun export a="$b"
), then the quotes are needed except in recent versions of dash
.
In the case of > /dev/null export a="$b"
, the quotes are needed in pdksh
and some of its derivatives.
For command export a="$b"
, the quotes are needed in every shell but mksh
, ksh93
, recent dash
, and bash -o posix
(with the same caveats about command
and export
not being the result of some expansion in shells other than dash
).
They are not needed in any shell when written:
foo=$var export foo
(that syntax being also compatible with the Bourne shell but in recent versions of zsh
, only working when in sh
/ksh
emulation).
(note that var=value local var
shouldn't be used as the behaviour varies across shells).
Also note that using export
with an assignment also means that the exit status of cmd
in export var="$(cmd)"
is lost. Doing it as export var; var=$(cmd)
doesn't have that problem.
Also beware of this special case with bash
:
$ bash -c 'IFS=; export a="$*"; echo "$a"' bash a b
ab
$ bash -c 'IFS=; export a=$*; echo "$a"' bash a b
a b
My advice would be to always quote.
I generally quote any usage of variables where there might be characters such as white spaces. Otherwise you'll run into problems like this:
#!/bin/bash
bar="hi bye"
function foo {
local myvar=${bar}
printf "%s\n" $myvar
printf "%s\n" "$myvar"
}
foo
The usage of the variable in an assignment doesn't appear to need the quotes, but when you go to use it such as in the printf
you'll need it quoted there:
printf "%s\n" "$myvar"
NOTE: Remember that the variable $IFS
is what governs what the separator characters are.
IFS The Internal Field Separator that is used for word splitting after
expansion and to split lines into words with the read builtin command.
The default value is ``<space><tab><newline>''.
Example
With debugging enabled in Bash we can see what's happening behind the scenes.
$ bash -x cmd.bash
+ bar='hi bye'
+ foo
+ local 'myvar=hi bye'
+ printf '%s\n' hi bye
hi
bye
+ printf '%s\n' 'hi bye'
hi bye
In the above we can see that the variable, $bar
was handed off fine to $myvar
but then when we went to use $myvar
we had to be cognoscente of the contents of $myvar
when we went to use it.