Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

bash and ~ expansion: cmd line vs. .profile

25 views
Skip to first unread message

Kenny McCormack

unread,
Feb 13, 2018, 11:35:25 AM2/13/18
to
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

In order to get it to work right, I have to use $HOME instead of ~.
No big deal, of course, except that it is more typing, and every so often,
I forget and do it the other way and find it not working.

Why the difference?

--
(Cruz certainly has an odd face) ... it looks like someone sewed pieces of a
waterlogged Reagan mask together at gunpoint ...

http://www.rollingstone.com/politics/news/how-america-made-donald-trump-unstoppable-20160224

Piotr Rogoza

unread,
Feb 13, 2018, 11:57:49 AM2/13/18
to
> But when I put this line in my .profile, it does not expand. The value
> of the variable is just: ~/lib

It works for me:
#--8<--
$ cat .bashrc
export VAR1=~/tmp/bin
$ . .bashrc
$ echo $VAR1
/home/dracorp/tmp/bin
#-->8---

Or are you talking about expansion inside a text file?
Another example:
#--8<--
$ cat .bashrc
export VAR1='~/tmp/bin'
$ . .bashrc
$ echo $VAR1
~/tmp/bin
#-->8---

--
piecia aka dracorp

Kenny McCormack

unread,
Feb 13, 2018, 12:05:03 PM2/13/18
to
In article <p5v5e7$91j$1...@dont-email.me>,
Piotr Rogoza <imie.r...@gmail.com> wrote:
>> But when I put this line in my .profile, it does not expand. The value
>> of the variable is just: ~/lib
>
>It works for me:

Dotting the file from the command line, well, doing it from the command line.
It is known to work when you do it at the command line.

The whole point is that there is something about the machine state in
effect at the point where the .profile is run that is causing the problem.

In order to test this effectively, you need to logout/login.

--
If Jeb is Charlie Brown kicking a football-pulled-away, Mitt is a '50s
housewife with a black eye who insists to her friends the roast wasn't
dry.

Piotr Rogoża

unread,
Feb 13, 2018, 12:27:07 PM2/13/18
to
On Tue, 13 Feb 2018 17:04:59 +0000, Kenny McCormack wrote:

> In order to test this effectively, you need to logout/login.
Yes, you are right but not 100%.
It does not expanse when I wrap ~ "" but it works without "".
Interesting.
I always use "" becasue it works with <c-w f> in Vim.

--
piecia aka dracorp
write to: name.r.public at gmail dot com

Kenny McCormack

unread,
Feb 13, 2018, 12:33:59 PM2/13/18
to
In article <p5v756$mlr$1...@dont-email.me>,
Piotr Rogoża <name.r...@gmail.com> wrote:
>On Tue, 13 Feb 2018 17:04:59 +0000, Kenny McCormack wrote:
>
>> In order to test this effectively, you need to logout/login.
>Yes, you are right but not 100%.
>It does not expanse when I wrap ~ "" but it works without "".
>Interesting.
>I always use "" becasue it works with <c-w f> in Vim.

I would never expect it to work inside quotes (either " or ').

But, to be clear, it does *NOT* work when you run .profile as part of a
normal login.

It does work, if you dot the file after having logged in, but that is
completely irrelevant.

--
I've learned that people will forget what you said, people will forget
what you did, but people will never forget how you made them feel.

- Maya Angelou -

Barry Margolin

unread,
Feb 13, 2018, 12:52:38 PM2/13/18
to
In article <p5v446$t9t$1...@news.xmission.com>,
gaz...@shell.xmission.com (Kenny McCormack) 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
>
> In order to get it to work right, I have to use $HOME instead of ~.
> No big deal, of course, except that it is more typing, and every so often,
> I forget and do it the other way and find it not working.
>
> Why the difference?

Are you sure bash is your login shell? Maybe you're logging into another
shell like /bin/sh, and then it's exec'ing bash.

--
Barry Margolin, bar...@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***

Kenny McCormack

unread,
Feb 13, 2018, 12:55:07 PM2/13/18
to
In article <barmar-A53FBD....@reader.eternal-september.org>,
Barry Margolin <bar...@alum.mit.edu> wrote:
...
>Are you sure bash is your login shell?

Yes.

>Maybe you're logging into another
>shell like /bin/sh, and then it's exec'ing bash.

Nope.

BTW, other bash-isms (such as shopt and $()) work just fine in .profile.

Nitpickers note: I can already hear someone saying that $() is POSIX, so it
should work even outside bash, but that is irrelevant. Trust me on that.

--
[Donald] Trump didn't have it all handed to him by his parents,
like Hillary Clinton did.

- Some dumb cluck in Ohio; featured in Michael Moore's "Trumpland" -

Janis Papanagnou

unread,
Feb 13, 2018, 1:01:37 PM2/13/18
to
On 13.02.2018 17:35, Kenny McCormack wrote:
> Observe:
>
> $ export zzz=~/lib
> [...]
>
> But when I put this line in my .profile, it does not expand. The value of
> the variable is just: ~/lib

Note that this works in .profile with Ksh, so it might be a bash bug, or,
just designed that way in bash.

>
> In order to get it to work right, I have to use $HOME instead of ~.

This is what I generally do in scripts to not get surprises depending on
shells used.

Janis

> [...]


Mike Sanders

unread,
Feb 13, 2018, 1:30:58 PM2/13/18
to
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
>
> In order to get it to work right, I have to use $HOME instead of ~.
> No big deal, of course, except that it is more typing, and every so often,
> I forget and do it the other way and find it not working.
>
> Why the difference?
>

(just guessing) tilde expansion happens before variable expansion?

<https://www.gnu.org/software/bash/manual/html_node/Tilde-Expansion.html>

--
later on,
Mike

https://busybox.hypermart.net

marrgol

unread,
Feb 13, 2018, 2:26:20 PM2/13/18
to
On 2018-02-13 at 18:52, Barry Margolin wrote:
>> $ export zzz=~/lib
>> $ echo $zzz
>> /home/myuser/lib
>> […]
>> 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
>> […]
>
> Are you sure bash is your login shell?

My thought too. Some distributions (e.g. debian, mint) use dash as
a default link to /bin/sh and dash behaves exactly as described above:
does not expand tilde in ~/.profile. bash does this as expected.


--
mrg

Helmut Waitzmann

unread,
Feb 13, 2018, 4:34:44 PM2/13/18
to
gaz...@shell.xmission.com (Kenny McCormack):
> 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

[…]

> Why the difference?

I made tests with 'bash' and 'dash'. When assigning and exporting
a variable by means of a

export variable=~/lib

statement, 'bash' (regardless of its invocation name being 'bash'
or 'sh') expanded the tilde, while 'dash' did not.

There was no difference, whether the assignment occurred within
the "${HOME%/}"/.profile file or later, for example within a
script that was read using the '.' command or in an interactive
shell reading commands from the terminal.

But if I replaced the

export variable=~/lib

command by

export variable && variable=~/lib

the tilde was expanded by 'dash' as well.

So I guess, the 'export' command is the culprit. Maybe that's a
bug in 'dash' (I don't know).

(As I generally try to support ancient shells in my scripts, I
usually do

var=value && export var

rather than

export var=value

and also "$HOME" rather than ~).

Kaz Kylheku

unread,
Feb 13, 2018, 8:30:32 PM2/13/18
to
On 2018-02-13, Barry Margolin <bar...@alum.mit.edu> wrote:
> Are you sure bash is your login shell? Maybe you're logging into another
> shell like /bin/sh, and then it's exec'ing bash.

That other shell is then neglecting to performing POSIX "tilde expansion".

Kaz Kylheku

unread,
Feb 13, 2018, 9:17:42 PM2/13/18
to
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.

Thomas 'PointedEars' Lahn

unread,
Feb 14, 2018, 4:21:20 AM2/14/18
to
Kenny McCormack wrote:

> In article <barmar-A53FBD....@reader.eternal-september.org>,
> Barry Margolin <bar...@alum.mit.edu> wrote:

Attribution line, not attribution novel.

>> Are you sure bash is your login shell?
>
> Yes.

Is bash invoked as an *interactive* login shell?

>> Maybe you're logging into another shell like /bin/sh, and then it's
>> exec'ing bash.
>
> Nope.
>
> BTW, other bash-isms (such as shopt and $()) work just fine in .profile.
>
> Nitpickers note: I can already hear someone saying that $() is POSIX, so
> it should work even outside bash, but that is irrelevant. Trust me on
> that.

“Talk is cheap. Show me the code.”

–Linus Torvalds

--
PointedEars
<https://github.com/PointedEars> | <http://PointedEars.de/wsvn/>
Twitter: @PointedEars2
Please do not cc me. /Bitte keine Kopien per E-Mail.

Thomas 'PointedEars' Lahn

unread,
Feb 14, 2018, 4:30:33 AM2/14/18
to
“~” can only be tilde-expanded in the standards-compliant way if the HOME
environment variable is set; if HOME is unset, it ought be expanded to the
HOME of the executing user if the shell is Bash 4.4 (as is documented).
However, according to POSIX.1-2008, “[i]f HOME is unset, the results are
unspecified.” [1]

Tilde expansion is tricky (in Bash) and *can* fail.

[1]
<http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_01>

and

RTFM (here: bash(1)).
0 new messages