On 2018-02-13, Kenny McCormack <
gaz...@shell.xmission.com> wrote:
> Observe:
>
> $ export zzz=~/lib
> $ echo $zzz
> /home/myuser/lib
> $ echo "$zzz"
> /home/myuser/lib
> $ export|grep zzz
> declare -x zzz="/home/myuser/lib"
> $ unset zzz
> $
>
> All working as expected.
>
> But when I put this line in my .profile, it does not expand. The value of
> the variable is just: ~/lib
I think that the clue to the puzzle may be this section in the Bash
man page:
Each variable assignment is checked for unquoted tilde-prefixes immedi‐
ately following a : or the first =. In these cases, tilde expansion is
also performed. Consequently, one may use filenames with tildes in
assignments to PATH, MAILPATH, and CDPATH, and the shell assigns the
expanded value.
The point is that there is something special about tilde in variable
assignments.
I'm looking at the bash internals, and I see that the tilde expansion
function, namely this:
char *bash_tilde_expand(const char *, int assign_p);
has this second parameter. (Contrary to the _p convention (from Lisp), it
is not a predicate, but three-valued).
If a variable assignment like A=~ is being expanded (as a whole), then
that tilde only gets expanded if assign_p is given a value of 1
(if we believe the block comment above the function).
Could it be that that all shells support tilde expansion in variable
assignments? And could it be that some version of Bash are deliberately
not doing this expansion for .profile, which is supposed to be shell-agnostic?
If you use .bash_profile instead, does the behavior reproduce?
(If .bash_profile is present, then .profile is ignored.)
---
Digging into this just a little deeper: the only place where bash_tilde_expand
is called with a nonzero assign_p is the expand_word_internal function.
This function makes a decision what assign_p value to pass based on some
logic that makes references to whether Bash is in POSIX mode. There is
a comment which reads:
/* If we're not in posix mode or forcing assignment-statement tilde
expansion, note where the `=' appears in the word and prepare to
do tilde expansion following the first `='. */
But, confusingly POSIX supports tilde expansion in assignments!
A "tilde-prefix" consists of an unquoted <tilde> character at the
beginning of a word, followed by all of the characters preceding the
first unquoted <slash> in the word, or all the characters in the word if
there is no <slash>. In an assignment (see XBD Variable Assignment),
multiple tilde-prefixes can be used: at the beginning of the word
(that is, following the <equals-sign> of the assignment), following
any unquoted <colon>, or both. A tilde-prefix in an assignment is
terminated by the first unquoted <colon> or <slash>.
I think this area of Bash has undergone some churn.
That assign_p was indeed a boolean. In bash 3.1, it grew the 2 value.
Buggy behavior was fixed to conform with POSIX. Tilde expansion doesn't
take place in words which *look* like assignments; they have to be assignments.
That is to say: command foo=~/blah doesn't expand.
Hacks were added to distinguish the case.