There are several aspects to this question as asked. In the first place, the effects of a quoted assignment might differ from the same assignment statement sans quotes for two reasons:
If the assignment contains delimiting syntax tokens (such as ;<>
or white-space) then quotes might serve to include these, whereas the unquoted assignment statement would delimit at the token.
In bash
and some other shells the tokens ()
are special cases in that var=(...)
serves to indicate an array assignment, though the same construct would be a syntax error without quotes in a POSIX shell which does not support the array extension.
( spc= sc=; echo "<$spc>:<$sc>"
spc=' ' sc=\;; echo "<$spc>:<$sc>"
)
<>:<>
< >:<;>
If the assignment contains expansion tokens quotes might serve to prohibit the expansion from taking place as might otherwise happen without them.
- Any tokens contained within an expansion do not apply as the contents of the expansion are literally passed from the expansion to the assigned variable.
The "
double-quote does not serve this purpose for $
denoted shell-expansions, and is therefore usually extraneous in an assignment statement, though it can be used to contain $
denoted expansions and other shell tokens in one quoted context.
( spc=\ sc=";"
tkns=$(printf "(<\n\'\t)>|")$spc$sc\"
names=tkns$spc$"sc$spc$"spc
printf ::$%s::\\n "$names" "$tkns"
)
::$tkns $sc $spc::
::$(<
\' )>| ;"::
Shell tokens are very special characters to the shell - they are the characters which separate commands each from one another, and command words from command arguments. They are the characters which delimit/concatenate shell words. Shell tokens are the characters which a shell's syntax parser (or lexer) will be looking for and acting upon as it reads in a command before ever the shell has a chance to consider expanding any part of it.
But there are other characters which are almost as special to the shell. These are characters which affect shell expansions, such as those contained within the value of the shell's $IFS
parameter, and filename expansion metacharacters like ?[*
, and - as noted in your question - the ~
tilde.
Excepting the ~
tilde in special cases, these characters have no bearing on assignment statements - the assignment context (as opposed to the list context) is immune to both $IFS
splitting and filename expansions. And so expansions which occur within a variable assignment need not be protected for literal values in the way you should in a command list.
And so the following works...
(meta=*?; meta=[$meta; echo "$meta")
[*?
The ~
tilde, though, is a special kind of shell expansion. Its expansion is handled very like a shell alias
in that its contents are always expanded literally. ~
tilde and alias
expansions both - like the more typical $
dollar-sign denoted expansions in assignment context - are always immune to $IFS
and filename expansions. They differ, though, in that the ~
tilde is almost always expanded as an argument, and a shell alias
cannot be expanded in that context unless it immediately follows another alias
which has been expanded and whose value ends with a <space>.
A ~
tilde expansion will not occur within quotes of any kind, and, for that matter, will not occur at all unless it is properly delimited on all sides, or unless its trailing context names a system user and the shell can properly confirm this fact.
And so...
~some_user
...will expand either to the home directory of the user some_user
if the shell can confirm such a user exists, or it will not expand at all.
To properly delimit a ~
tilde expansion you can use any of the shell tokens already mentioned excepting the quotes plus a /
on the right-hand side in a list context, or, in an assignment context, either of /
or :
on the right-hand side or, on the left, either the first =
in the assignment statement or :
anywhere else.
When there is no trailing context, and the ~
tilde is properly delimited, then the ~
tilde will expand to the current value of the shell variable $HOME
. This distinction is often mistakenly overlooked when discussing the ~
tilde expansion. A user's home directory and the value in $HOME
need not be the same, as the shell variable $HOME
can be reassigned or unset
at any time like any other. In fact, when $HOME
is unset
, POSIX leaves the behavior of a lone ~
tilde's expansion unspecified, though bash
, for example, will attempt a lookup with the system for the current user's home directory.
For example:
HOME="
:~"::~//~ bash -c '
printf "\t<%s>" ~/ ~mikeserv/
HOME=; echo
printf "\t<%s>" ~/ ~mikeserv/
unset HOME; echo
printf "\t<%s>" ~/ ~mikeserv/
'
<
:~::/home/mikeserv//~/> </home/mikeserv/>
</> </home/mikeserv/>
</home/mikeserv/> </home/mikeserv/>
echo $TEMP | ls
is not doing what you think.ls
doesn't read from stdin, so the$TEMP
piped to it is ignored. Try running the command when$TEMP
is a different path than the current directory and you'll see.