39

I know this has probably been asked before, but I couldn't find it with Google.

Given

  • Linux Kernel
  • No configurations that change $HOME
  • bash

Will ~ == $HOME be true?

  • 3
    I believe so, but this is not a Linux-specific issue. Rather, I believe ~ will be equivalent to $HOME in any POSIX environment; but I could be wrong. – HalosGhost Jul 26 '14 at 3:11
  • 4
    Despite the answers below… not always. Compare echo "~" and echo "$HOME". – Sparhawk Jul 26 '14 at 3:46
  • @Sparhawk Wait, so 1+1 isn't 2? It's a conspiracy! The government is hiding something from us! :P – Doorknob Jul 26 '14 at 6:13
  • 1
    @Sparhawk, you'll notice the OP actually did not quote either the ~ or $HOME. :P – HalosGhost Jul 26 '14 at 7:51
  • 2
    @HalosGhost Ah, good point. (Also, I later noticed that Michael Homer's answer mentions "a single unquoted ~".) Anyway, something to keep in mind for the casual reader, like myself. – Sparhawk Jul 26 '14 at 7:55
45

What's important to understand is that ~ expansion is a feature of the shell (of some shells), it's not a magic character than means your home directory wherever it's used.

It is expanded (by the shell, which is an application used to interpret command lines), like $var is expanded to its value under some conditions when used in a shell command line before the command is executed.

That feature first appeared in the C-shell in the late 1970s (the Bourne shell didn't have it, nor did its predecessor the Thompson shell), was later added to the Korn shell (a newer shell built upon the Bourne shell in the 80s). It was eventually standardized by POSIX and is now available in most shells including non-POSIX ones like fish.

Because it's in such widespread use in shells, some non-shell applications also recognise it as meaning the home directory. That's the case of many applications in their configuration files or their own command line (mutt, slrn, vim...).

bash specifically (which is the shell of the GNU project and widely used in many Linux-based operating systems), when invoked as sh, mostly follows the POSIX rules about ~ expansion, and in areas not specified by POSIX, behaves mostly like the Korn shell (of which it is a part clone).

While $var is expanded in most places (except inside single quotes), ~ expansion, being an afterthought is only expanded in a few specific conditions.

It is expanded when on its own argument in list contexts, in contexts where a string is expected.

Here are a few examples of where it's expanded in bash:

  • cmd arg ~ other arg
  • var=~
  • var=x:~:x (required by POSIX, used for variables like PATH, MANPATH...)
  • for i in ~
  • [[ ~ = text ]]
  • [[ text = ~ ]] (the expansion of ~ being taken as a pattern in AT&T ksh but not bash since 4.0).
  • case ~ in ~) ...
  • ${var#~} (though not in some other shells)
  • cmd foo=~ (though not when invoked as sh, and only when what's on the left of the = is shaped like an unquoted bash variable name)
  • cmd ~/x (required by POSIX obviously)
  • cmd ~:x (but not x:~:x or x-~-x)
  • a[~]=foo; echo "${a[~]} $((a[~]))" (not in some other shells)

Here are a few examples where it's not expanded:

  • echo "~" '~'
  • echo ~@ ~~ (also note that ~u is meant to expand to the home directory of user u).
  • echo @~
  • (( HOME == ~ )), $(( var + ~ ))
  • with extglob: case $var in @(~|other))... (though case $var in ~|other) is OK).
  • ./configure --prefix=~ (as --prefix is not a valid variable name)
  • cmd "foo"=~ (in bash, because of the quotes).
  • when invoked as sh: export "foo"=~, env JAVA_HOME=~ cmd...

As to what it expands to: ~ alone expands to the content of the HOME variable, or when it is not set, to the home directory of the current user in the account database (as an extension since POSIX leaves that behaviour undefined).

It should be noted that in ksh88 and bash versions prior to 4.0, tilde expansion underwent globbing (filename generation) in list contexts:

$ bash -c 'echo "$HOME"'
/home/***stephane***
$ bash -c 'echo ~'
/home/***stephane*** /home/stephane
$ bash -c 'echo "~"'
~

That should not be a problem in usual cases.

Note that because it's expanded, the same warning applies as other forms of expansions.

cd ~

Doesn't work if $HOME starts with - or contains .. components. So, even though it's very unlikely to ever make any difference, strictly speaking, one should write:

cd -P -- ~

Or even:

case ~ in
  (/*) cd -P ~;;
  (*) d=~; cd -P "./$d";;
esac

(to cover for values of $HOME like -, +2...) or simply:

cd

(as cd takes you to your home directory without any argument)

Other shells have more advanced ~ expansions. For instance, in zsh, we have:

  • ~4, ~-, ~-2 (with completion) used to expand the directories in your directory stack (the places you've cd to before).
  • dynamic named directories. You can define your own mechanism to decide how ~something is being expanded.
25

In any version of Bash on any system, yes. ~ as a term on its own is defined to expand to:

The value of $HOME

so it will always be the same as whatever $HOME is to the current shell. There are several other tilde expansions, such as ~user for user's home directory, but a single unquoted ~ on its own will always expand to "$HOME".

Note that the behaviour of ~ and $HOME can be different in some cases: in particular, if $HOME contains spaces (or other IFS characters), then $HOME (unquoted) will expand to multiple words, while ~ is always a single word. ~ expands equivalently to "$HOME" (quoted).

In regard to your specific question:

[[ $HOME == ~ ]]

is always true, because [[ suppresses word-splitting. [[ ~ == $HOME ] may not be if HOME has pattern matching characters in it, but [[ ~ == "$HOME" ]] (i.e., quoted "$HOME") is always true. Using it inside single brackets can be a syntax error for values of HOME containing spaces or special characters. For any sensible home directory configuration ~ and "$HOME" are the same and compare as equal.


Stéphane Chazelas has noted a case in the comments where ~ and $HOME give different values: if you unset HOME, then when you use ~ Bash will call getpwuid to read a value out of the password database. This case is excluded by your condition of having no configuration changing $HOME, but I'll mention it here for completeness.

  • 1
    However, /bin/sh might not be bash. I'm not sure that Posix sh specification tells about ~ – Basile Starynkevitch Jul 26 '14 at 5:38
  • 1
    POSIX specifies ~. ~ was not in the Thomson or Bourne shell (that in their time were available as /bin/sh). It's not in rc or its derivatives (where it's used for something else) – Stéphane Chazelas Jul 26 '14 at 6:47
  • 5
    In a few shells including bash, if HOME is unset, ~ expands to the home directory of the user from the passwd database. So that's a case where ~ may not expand to the value of $HOME. – Stéphane Chazelas Jul 26 '14 at 6:55
  • 1
    Note that bash before bash4 used to perform globbing upon tilde expansion (try HOME='/*' bash -c 'echo /*'). So HOME=/*; [ "$HOME" = ~ ] would return an error there. – Stéphane Chazelas Jul 26 '14 at 7:23
  • 4
    @cuonglm I checked the source code. It ultimately calls get_current_user_info, which uses getpwuid on all platforms but Tandem. – Michael Homer Oct 13 '15 at 19:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.