33

Writing my first script so I'm sure this is a basic question, but can someone please explain to me why I can:

cd ~
cd bin
cd ~/bin
cd 'bin'

But not

cd '~'
cd '~/bin'

I need to cd to a directory path with a space in one of the directory names, so I need the quotes (it's Windows Program Files under wine). I can get around it with two cd commands, but why can't I put ~ in quotes?

If I type cd '~' (or cd "~") I get:

bash: cd: ~: No such file or directory
  • 29
    Ask yourself this: if if didn't work that way, how would you cd into a directory named ~? – Jörg W Mittag May 5 '18 at 15:11
  • 2
    As a sidenote, if you're writing a script you should rather use absolute paths and avoid cd alltogether. Use variables to store pathnames you don't want to type multiple times, e.g. pf=~/.wine/drive_c/Program\ Files/; cp /path/to/file "$pf" – dessert May 5 '18 at 15:40
  • 8
    @jamesqf: Why would a sensible system presume to know better than me what I want to name my files and directories? Also, I never understood why Unix went with magic in-band signaling of path component separators and path terminators. What's wrong with a file/directory named / or NUL? – Jörg W Mittag May 5 '18 at 17:40
  • 3
    @JörgWMittag presumably cd ./~ – k_g May 6 '18 at 3:31
  • 2
    @jamesqf Just to note that historically, Unix filesystems allowed any char in a filename except '/' (path separator) and '\0' (NUL - string-termination char) - as Jorg notes. No other restrictions. There have been many Unix filesystem implementations, so introducing restrictions in any one of them would have broken compatibility and increased complexity - and been seen as "weird"! And '~' expansion was only introduced comparatively recently in csh and more widely in bash, files/dirs with '~' chars in are not unknown (eg myfile~ as a backup for myfile). – SusanW May 7 '18 at 12:53
79

As @karel noted in his answer, ~ is a special character and expanded by Bash into the current user's home directory. See Bash's manual on "Tilde Expansion", or search for the headline "Tilde Expansion" in the man page (man bash).

Any kind of quotation around the ~ prevents this tilde expansion.


To answer your question about how you still can use it to cd into a directory with spaces in its name, there are a few alternatives:

  • Omit quotes and escape the spaces with backslashes instead:

    cd ~/foo/spaces\ are\ cool/bar
    
  • Quote the rest of the path, but omit them around the tilde and the first slash:

    cd ~/"foo/spaces are cool/bar"
    

    As you see, you can concatenate quoted and unquoted strings in Bash by simply writing them next to each other without any spaces in between.

  • Use the environment variable $HOME instead of the tilde, which still gets expanded inside "double quotes" (but not 'single quotes'):

    cd "$HOME/foo/spaces are cool/bar"
    
  • 3
    @rexkogitans ~'/...' won't work and isn't what this answer has. ~/'...' or ~/"..." will work. – hvd May 5 '18 at 13:33
  • tld;dr: because thats shell syntax – Sergiy Kolodyazhnyy May 5 '18 at 14:48
  • @hvd thanks. Fixed typo. This is the most complete answer and also explains how to solve what the OP wants to do. ~/'path' is the way to go. – rexkogitans May 5 '18 at 14:49
  • 1
    @hvd just little of curiosity, why wouldn't ~'/...' work? The slash isn't a special character so it seems it should work either inside or outside the quotes. – Kodos Johnson May 5 '18 at 18:41
  • 6
    @KodosJohnson Have a look at the Bash manpage's section about "Tilde Expansion". The relevant paragraph of that is also quoted in dessert's answer below. TL;DR: Tilde expansion takes place if the first character in a word is an unquoted tilde, then everything between that tilde and the next unquoted slash is considered the "tilde prefix". If this prefix is not a valid thing like +, -, a number, or a user name (quotes in there are not allowed either), the expansion fails and you get a literal ~ character. – Byte Commander May 5 '18 at 18:52
17

~ is a special character that is interpreted by the shell as the logged in user's home directory. '~' is interpreted by the shell as a literal ~ character, not as the logged in user's home directory because enclosing a string inside two single quote characters results in that string being interpreted as a literal text string.

  • ~ is not an alias in the sense bash interprets it: Aliases allow a string to be substituted for a word when it is used as the first word of a simple command.. As I explain in my answer below, it's rather an expansion. – dessert May 5 '18 at 11:38
15

This is a bash feature called Tilde Expansion. Citing man bash:

If a word begins with an unquoted tilde character (`~'), all of the characters preceding the first unquoted slash (or all characters, if there is no unquoted slash) are considered a tilde-prefix. 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. If this login name is the null string, the tilde is replaced with the value of the shell parameter HOME. If HOME is unset, the home directory of the user executing the shell is substituted instead.

For the expansion to work the tilde character ~ needs to be unquoted, else the character is taken literally and cd fails if there is no directory named ~ present in the current directory. See this entensive answer for an explanation of quoting in bash. If you need to quote part of the path, you can therefore:

  1. quote at least the characters that need quoting with single quotes, e.g.

    ~/dir' 'with' 'spaces/
    

    or

    ~/'dir with spaces/'
    
  2. quote at least the characters that need quoting with double quotes, e.g.

    ~/dir" "with" "spaces/
    

    or

    ~/"dir with spaces/"
    
  3. quote only the characters that need quoting with backslashes , e.g.

    ~/dir\ with\ spaces/
    

Tilde Expansion has some more interesting features, e.g.:

  • ~+ expands to the value of PWD, i.e. the current working directory
  • ~- expands to the value of OLDPWD, i.e. the previous working directory
  • ~john expands to the home directory associated with the login name “john”
  • Incidentally, it is OK to 'overquote' with backslashes, too; e.g., $ touch a; cd \a. (I thought this might run into trouble with cd \n, but experimentation at least on my version of bash suggests it works fine.) – LSpice May 7 '18 at 14:57
1

Explore using echo command

The easiest way of exploring how things work in bash is with the echo command. In the case of ~ use this:

$ echo ~
/home/rick
$ echo '~'
~
$ echo "~"
~
$ echo `~`
bash: /home/rick: Is a directory

As you can see, when you single quote or use double quotes around ~ it is interpreted literally as a string and not expanded as a variable. When you use backticks (`) it is executed as a command and generates an error message.

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.