76

According to this answer and my own understanding, the tilde expands to the home directory:

$ echo ~
/home/braiam

Now, whenever I want the shell expansion to work, i. e. using variable names such $FOO, and do not break due unexpected characters, such spaces, etc. one should use double quotes ":

$ FOO="some string with spaces"
$ BAR="echo $FOO"
$ echo $BAR
echo some string with spaces

Why doesn't this expansion works with the tilde?

$ echo ~/some/path
/home/braiam/some/path
$ echo "~/some/path"
~/some/path
Reid
  • 460
  • 5
  • 14
Braiam
  • 35,380
  • 25
  • 108
  • 167
  • 1
    http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_01 – llua Aug 24 '14 at 01:12
  • 3
    Also note that this has the inconsistency when providing a program argument on command line that on command argument `--path ~/myfile` expands but `--path=~/myfile` doesn't. – Ángel Aug 24 '14 at 18:59
  • 1
    Related: [Does ~ always equal $HOME](http://unix.stackexchange.com/a/146697) – Stéphane Chazelas Aug 18 '16 at 10:41
  • Related (on [ubuntu.se]): [Why does mkdir fail (no such file or directory) in a script with BIN_DIR=“~/bin/”?](https://askubuntu.com/questions/977354/mkdir-command-fails-no-such-file-or-directory-in-script-with-bin-dir-bin) – Eliah Kagan Nov 17 '17 at 12:49
  • A variation on this theme is https://unix.stackexchange.com/questions/279565/ . – JdeBP Jan 26 '18 at 16:38
  • VERY thorough set of solutions at [How to manually expand a special variable (ex: ~ tilde) in bash](https://stackoverflow.com/questions/3963716/how-to-manually-expand-a-special-variable-ex-tilde-in-bash) – Reed Sep 01 '20 at 20:19

4 Answers4

67

The reason, because inside double quotes, tilde ~ has no special meaning, it's treated as literal.

POSIX defines Double-Quotes as:

Enclosing characters in double-quotes ( "" ) shall preserve the literal value of all characters within the double-quotes, with the exception of the characters dollar sign, backquote, and backslash,

...

The application shall ensure that a double-quote is preceded by a backslash to be included within double-quotes. The parameter '@' has special meaning inside double-quotes

Except $, `, \ and @, others characters are treated as literal inside double quotes.

Lucio
  • 1,267
  • 1
  • 13
  • 16
cuonglm
  • 150,973
  • 38
  • 327
  • 406
  • 18
    In which case I guess you should use `$HOME`. – Seth Aug 24 '14 at 20:29
  • 18
    Or you can just not quote the `~`, for example `ls -l ~/"My Documents"` – nobody Aug 24 '14 at 21:47
  • This is very non-intuitive. What is the reason they chose to do this? (This answer doesn't actually give the reason, but rather just points to the standard, but presumably the standard was written that way for a *reason*.) – iconoclast Jan 16 '15 at 02:56
  • 1
    @iconoclast: The quesion is `Why doesn't the tilde (~) expand inside double quotes?` ==> Because `~` is treated as literal inside quotes ==> Why? Because it's defined that way. What is non-intuitive here when the question doesn't ask about the reason POSIX chose to do that? – cuonglm Jan 16 '15 at 03:23
  • 3
    @cuonglm: that's like telling your kids, when they ask why the sky is blue, that it's blue because the blue wavelengths are hitting their retinas. It's true and gives the immediate reason, but a useful and satisfying reason has to go deeper to give a real *reason* rather than the immediately preceding cause in a long causal chain. The initial cause in a long chain is more useful than the immediately preceding cause, or at least one further back to give a logical reason than just "because that's the way it works". – iconoclast Jan 16 '15 at 16:12
  • Oh yes, in this case, I don't know anything about real reason. Can you share it? – cuonglm Jan 16 '15 at 16:18
  • 2
    @iconoclast if you really want a "why they got implemented this way" read [Stephane](http://unix.stackexchange.com/a/151967/41104) answer instead. – Braiam Mar 08 '15 at 16:46
  • 4
    So, @iconoclast, why _is_ the sky blue? :) :) :) – Jesse Chisholm Mar 28 '16 at 18:10
42

Tilde expansion is defined by POSIX as:

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, multiple tilde-prefixes can be used: [...] following the <equals-sign> of the assignment, following any unquoted <colon>, or both. [...] If none of the characters in the tilde-prefix are quoted, the characters in the tilde-prefix following the <tilde> are treated as a possible login name from the user database. [...] If the login name is null (that is, the tilde-prefix contains only the tilde), the tilde-prefix is replaced by the value of the variable HOME. If HOME is unset, the results are unspecified. [...]

So the shortest answer is "because it's defined that way": quoting any of the characters in the prefix, including the ~, suppresses expansion.

It also defines the expansion as always resulting in a single word, so quoting would be unnecessary:

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

Where some of the path requires quoting, but the rest is a tilde prefix, you can combine tilde expansion and ordinary quoting straightforwardly:

$ cat ~/"file name with spaces"

On the broader "why": since there's no conceivable use for word-splitting ~, that should be the default behaviour, rather than requiring it to be quoted. Because there's no need to quote it, giving ~ a special meaning inside quotes would be an unnecessary complication. And, of course, historical reasons mean it couldn't be changed now even if that were desirable.

Michael Homer
  • 74,824
  • 17
  • 212
  • 233
  • According to [this bash guide](http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html), tilde expansion happens *before* whitespace separation. Is there a way to safely do a tilde expansion, even if you home directory has whitespace in it? Ordinarily, of course, you'd do this sort of thing with `""`. – Lucretiel Aug 21 '15 at 18:53
  • The expansion is a single word; see the second quoted passage in the answer. – Michael Homer Aug 21 '15 at 20:38
28

~ originates in the C-shell, long before it was added to the Korn shell and later added to the POSIX shell specification.

In the C-shell, ~ was a globbing operator (expanded by the same routine as the one expanding *.txt for instance), so like the rest of the globbing was not performed inside double quotes.

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
17

While this doesn't answer why it's designed that way, you use $HOME instead if you need to substitute, since that's essentially what ~ does.

$ echo "$HOME/some/path"
/home/braiam/some/path
melds
  • 366
  • 1
  • 9