Ubuntu – When should I use the ~ (tilde) during command line navigation

bashcommand line

Pretty stupid when it comes to linux and I'm trying to figure out navigation with the command line. I can move back and forth from my home folder and the folders inside pretty easily with this:

cd ~/Documents/

but when I go to open a folder in documents i'm getting an error like this:

chance@asus:~/Documents$ cd ~/xmr-stak-cpu-1.3.0-1.5.0
bash: cd: /home/chance/xmr-stak-cpu-1.3.0-1.5.0: No such file or directory

I'm confused because I need to use "~" to get into documents, but I cannot navigate into a subsequent folder while in documents.

Best Answer

TL;DR

  • path is read left to right, with leftmost directory specified being the top of the directory tree you're traversing
  • leading ~/ in path means your home folder
  • leading ./ means in this folder or stuff in current working directory
  • navigating to a directory can be done via giving path relative to your current working directory, or specifying full path
  • ~ is relevant in command-line only, not in GUI (as mentioned in comments).

The answer is edited to include more formal explanation of tilde expansion and to suit the new edited title of the question, so please read the more formal explanation below. First two sections are more or less gentle explanations of what OP did and didn't do.

The problem

The leading ~ (tilde) followed by slash in path is understood as reference to your user's home directory, i.e. ~/Documents always means /home/chance/Documents. (There are exceptions and special cases but for the purpose of explaining the basics, let's keep it simple so far; see below for the deeper explanation).

So when you do cd ~/xmr-stak-cpu-1.3.0-1.5.0, you're saying take me to xmr-stak-cpu-1.3.0-1.5.0 directory that is located in my home folder. What also is important to note, is that a path is always read left to right, separated by slashes, with the leftmost being the top of the directory tree that you reference. So if you want to descend from home, to Documents, to your folder, all pieces must be present in the path, separated by slashes, and the folder you want to end up in, of course, has to be the right most.

Look at the error message closely:

bash: cd: **/home/chance/xmr-stak-cpu-1.3.0-1.5.0**: No such file or directory

The shell properly expanded ~ to be /home/chance and joined that with what you gave it. Of course, because the directory you specified doesn't exist, you got the appropriate error message.

What you should have done

You can do it in one of two ways - give full path or jump into ~/Documents first, then jump to directory you want. When you cd ~/Documents and want to navigate to folder that's located inside the current directory that you're at, you can use ./ notation. So you would do:

$ cd ~/Documents
$ cd ./xmr-stak-cpu-1.3.0-1.5.0/

Of course you don't have to add that ./, when you simply do cd xmr-stak-cpu-1.3.0-1.5.0 will automatically look inside your current working directory. However, it's better to be explicit, and besides ./ helps avoiding issues with filenames that might start with - (which is a whole different bag of problems and is a topic for another day).

Alternatively, you can give full path:

$ cd ~/Documents/xmr-stak-cpu-1.3.0-1.5.0/

Note that you can also use the $HOME variable instead of ~, which is pretty much the same in effect, however they differ in nature and in the extra stuff that you can do with ~. See my answer on Difference cd and cd ~.

More formal and in-depth explanation

Alright, so far we've kept things fairly simple for the purpose of explaining basics to new users. But there's much more to tilde, dot-slash, and navigating the directory tree than that. Matters get more complicated when we consider that tilde and cd behave differently in other shells, and if we also consider the POSIX standard. Some of the things here I've already touched on in my related answer on Unix & Linux, so be sure to check that out as well.

In Bourne-like shells (that is bash, ksh, ksh-related implementations like mksh, and Ubuntu's default /bin/sh, which is actually dash) the unquoted leading tilde signifies Tilde Expansion, which is in fact specified as standard behavior by POSIX. Depending on what follows an unquoted leading tilde, it has different effects:

  • with an unquoted leading ~, when there are no characters following it (or if there's nothing between ~ and next unquoted /, i.e. a null string) it expands to the same value as $HOME environment variable, i.e. your home folder. So cd ~ and cd ~/ mean change directory to your home folder. Note that ~ and / should be unquoted:

    $ bash -c 'cd /etc/; ls -d ~'
    /home/xieerqi
    $ bash -c 'cd /etc/; ls -d ~/'
    /home/xieerqi/
    $ bash -c 'cd /etc/; ls -d ~"/"'
    ls: cannot access '~/': No such file or directory
    $ bash -c 'cd /etc/; ls -d "~"/'
    ls: cannot access '~/': No such file or directory
    
  • with an unquoted leading ~, the collection of characters that follow it are considered a possible login name. So cd ~testuser or cd ~testuser/ would mean you want to jump into testuser's current working directory. Again, quoting matters:

    $ bash -c 'cd /etc/; ls -d ~testuser'
    /home/testuser
    $ bash -c 'cd /etc/; ls -d ~"testuser"'
    ls: cannot access '~testuser': No such file or directory
    $ bash -c 'cd /etc/; ls -d ~testuser"/"'
    ls: cannot access '~testuser/': No such file or directory
    

This is all standard behavior and works across the board in dash, ksh, and of course bash. However, there are a few things that shells do differently. In particular, bash has extra expansions that you can perform with tilde, and handles an unset HOME differently than other shells. Here are the extra expansions you can do in bash:

  • ~+ references your current working directory, same as PWD variable. This also works in ksh but not dash.

    $ bash -c 'echo ~+; unset PWD;echo ~+'
    /home/xieerqi
    ~+
    
  • ~- references your previous working directory, same as OLDPWD variable. Also works in ksh but not dash.

    $ bash -c 'cd /etc/;cd /usr; echo ~-; unset OLDPWD;echo ~-'
    /etc
    ~-
    
  • In bash you can navigate AND record where you've been by pushing specific directories on the directory stack (or array), and you can reference them by ~[+-]<NUMBER>, where + would mean looking from the left of the array, and - from the right. This is bash specific and doesn't work work in ksh nor dash.

    $ bash -c 'pushd /etc/; pushd /usr; echo ~+1'
    /etc ~
    /usr /etc ~
    /etc
    

    See diagram in muru's answer to a related question, which shows how to use indexes for ~+ and ~-.

Another thing that I've already hinted at is the unset HOME environment variable. The fact that it is a variable and can be unset poses a problem - as specified by POSIX standard, tilde expansion depends on the environment variables, but according to the standard "[i] HOME is unset, the results are unspecified." In this case, ksh and dash simply break:

$ ksh -c 'cd /usr; unset HOME; cd ~'
ksh: cd: xieerqi: [No such file or directory]

$ dash -c 'cd /usr; unset HOME; cd ~'
dash: 1: cd: can't cd to ~

In fact, this is in accordance with the standard for cd as well:

  1. If no directory operand is given and the HOME environment variable is empty or undefined, the default behavior is implementation-defined and no further steps shall be taken.

By contrast bash, handles things differently. According to the bash manual:

If HOME is unset, the home directory of the user executing the shell is substituted instead. Otherwise, the tilde-prefix is replaced with the home directory associated with the specified login name.

In other words, when home is unset only, bash won't break, but if HOME is set to the null string, it will break:

$ bash -c 'cd /etc; unset HOME; cd ~; pwd'
/home/xieerqi
$ bash -c 'cd /etc; HOME=""; cd ~; pwd'
/etc

Fun fact

~ was chosen to represent user's home directory because one upon a time ~ and HOME keys used to reside on the same keyboard key on Lier-Siegler ADM-3A terminal. (source)

See also: