90

I had always thought that $HOME and ~ were exactly the same and thus could be used interchangeably. Today, when I tried to install pylibmc, a python binding to memcached, on my shared server the use of ~ gave me error but not $HOME. I would like to reason out why.

libmemcached is a requirement for pylibmc. I have libmemcached installed under my home directory because I have no root on the server. As a result, to install pylibmc, I need to make sure the installation script knows where to find libmemcached.

When executing python setup.py install --with-libmemcached=~, the installation script runs

gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall \
  -Wstrict-prototypes -fPIC -DUSE_ZLIB -I~/include \
  -I/usr/local/include/python2.7 -c _pylibmcmodule.c \
  -o build/temp.linux-i686-2.7/_pylibmcmodule.o -fno-strict-aliasing

which gives the errors that libmemcached can't be found.

If I change to --with-libmemcached=$HOME, the script runs

gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall \
  -Wstrict-prototypes -fPIC -DUSE_ZLIB -I/home/waterbotte/include \
  -I/usr/local/include/python2.7 -c _pylibmcmodule.c \
  -o build/temp.linux-i686-2.7/_pylibmcmodule.o -fno-strict-aliasing

without any problem. It looks like the problem is that tilde doesn't get resolved. But why?

Zombo
  • 1
  • 62
  • 391
  • 407
tamakisquare
  • 16,659
  • 26
  • 88
  • 129

5 Answers5

60

The tilde is part of a shell expansion (like in bash, csh, zsh, etc). The $HOME variable is exportable and can be used independent of a specific shell.

Jon Lin
  • 142,182
  • 29
  • 220
  • 220
44

~ is expanded ONLY if it is the first character of a word AND it is unquoted

$ echo "~"
~
$ echo foo~
foo~
$ echo ~
/home/guest
$ echo ~/foo
/home/guest/foo

~username is expanded to the HOME of the username.

$ echo ~root
/root
$ echo ~invaliduser
~invaliduser

To quote filenames, you should use $HOME or quote the suffix

$ echo "$HOME/foo bar"
/home/guest/foo bar
$ echo ~/"foo bar"
/home/guest/foo bar
$ echo ~root/"foo bar"
/root/foo bar

Note the following from "POSIX Tilde Expansion"

The pathname resulting from tilde expansion shall be treated as if quoted to prevent it being altered by field splitting and pathname expansion.

go2null
  • 2,080
  • 1
  • 21
  • 17
40

The shell replaces ~ with the user's home directory (update: or perhaps by the home directory of some other user, if ~ is followed by something other than a /), but only if it's the first character of a word.

--with-libmemcached=~ has ~ not in the beginning, so the shell leaves it alone.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • 2
    stromberg@aw50 ~ $ echo abc~def abc~def stromberg@aw50 ~ $ echo ~def ~def stromberg@aw50 ~ $ echo def~ def~ stromberg@aw50 ~ $ echo abc${HOME}def abc/home/strombergdef stromberg@aw50 ~ $ echo ${HOME}def /home/strombergdef stromberg@aw50 ~ $ echo def${HOME} def/home/stromberg stromberg@aw50 ~ $ – user1277476 Jul 20 '12 at 22:00
  • @user1277476: This is very hard to read. Please see the update. – n. m. could be an AI Jul 20 '12 at 22:03
  • Interesting fact to learn. Exactly what I am looking for. Thanks n.m. – tamakisquare Jul 20 '12 at 22:24
  • @user1277476 `echo` is a built in shell command, the shell knows if you want to `echo` something, the argument is not referring to a file, since `~` is a built in shell expansion, the shell knows better and won't replace it with your home directory. – Jon Lin Jul 21 '12 at 05:34
  • It should be mentioned that there's more to tilde expansion than this. See the bash manual on [Tilde Expansion](https://www.gnu.org/software/bash/manual/html_node/Tilde-Expansion.html). For instance, tilde is expanded when used in variable assignment (e.g. `SOME=~`) even though it's not the first character of a word then. – user_ Apr 21 '20 at 18:38
11

The main difference is:

cd /tmp
ls "$HOME" #works
ls "~" #nope

So, shell expand the ~ only in few situations. In your case, the python script simple got ~ inside the script - not the expaded value.

clt60
  • 62,119
  • 17
  • 107
  • 194
3

Run the following script:

#!/bin/bash

sudo -H -u root bash<<EOF
echo $HOME
echo ~
EOF

Output:

/home/my_current_user
/root

You can see that ~ gets expanded later, by the target shell (run by root) while $HOME gets substituted by the source shell (run by my_current_user)

Rufus
  • 5,111
  • 4
  • 28
  • 45