54

The Bash command

cd - 

prints the previously used directory and changes to it.

On the other hand, the Bash command

cd ~-

directly changes to the previously used directory, without echoing anything.

Is that the only difference? What is the use case for each of the commands?

  • 2
    I recommend you don't use any of this tricky stuff. This is your brain on cd ~-. – DepressedDaniel Dec 17 '16 at 6:36
  • 10
    @DepressedDaniel, even if you don't (plan to) use tricky stuff, it's still useful to understand what's going on behind the scenes — understanding corner cases can get you out of a hole later on when trying to debug something different. – anotherdave Dec 17 '16 at 9:47
79

There are two things at play here. First, the - alone is expanded to your previous directory. This is explained in the cd section of man bash (emphasis mine):

An argument of - is converted to $OLDPWD before the directory change is attempted. If a non-empty directory name from CDPATH is used, or if - is the first argument, and the directory change is successful, the absolute pathname of the new working directory is written to the standard output. The return value is true if the directory was successfully changed; false otherwise.

So, a simple cd - will move you back to your previous directory and print the directory's name out. The other command is documented in the "Tilde Expansion" section:

If the tilde-prefix is a ~+, the value of the shell variable PWD replaces the tilde-prefix. If the tilde-prefix is a ~-, the value of the shell variable OLDPWD, if it is set, is substituted. If the characters following the tilde in the tilde-prefix consist of a number N, optionally prefixed by a + or a -, the tilde-prefix is replaced with the corresponding element from the directory stack, as it would be displayed by the dirs builtin invoked with the tilde-prefix as an argument. If the characters following the tilde in the tilde-prefix consist of a number without a leading + or -, + is assumed.

This might be easier to understand with an example:

$ pwd
/home/terdon
$ cd ~/foo
$ pwd
/home/terdon/foo
$ cd /etc
$ pwd
/etc
$ echo ~        ## prints $HOME
/home/terdon
$ echo ~+       ## prints $PWD
/etc
$ echo ~-       ## prints $OLDPWD
/home/terdon/foo

So, in general, the - means "the previous directory". That's why cd - by itself will move you back to wherever you were.

The main difference is that cd - is specific to the cd builtin. If you try to echo - it will just print a -. The ~- is part of the tilde expansion functionality and behaves similarly to a variable. That's why you can echo ~- and get something meaningful. You can also use it in cd ~- but you could just as well use it in any other command. For example cp ~-/* . which would be equivalent to cp "$OLDPWD"/* .

  • 1
    set -x to print the expanded command before executing it might be useful to illustrate the difference (or might just be unreadably noisy when you're not doing it yourself one step at a time). – Peter Cordes Dec 17 '16 at 18:08
  • 1
    @terdon today I learned. – sitilge Feb 20 '17 at 11:07
  • In short... cd ~-/.. works, cd -/.. does not! – Ray Foss Jun 7 at 14:27
13

~- is subject to tilde expansion (see man bash), so what cd sees is the previous directory name directly. - is not expanded by the shell, cd sees it directly, and behaves as documented:

An argument of - is equivalent to $OLDPWD. If a non-empty directory name from CDPATH is used, or if - is the first argument, and the directory change is successful, the absolute pathname of the new working directory is written to the standard output.

4

TL;DR: cd - is built into cd, ~- is a later extension of ~[name], not specific to cd.

POSIX.1-2008 Shell & Utilities defines cd - as a special case that is specific for the cd command:

When a - is used as the operand, this shall be equivalent to the command:

cd "$OLDPWD" && pwd

which changes to the previous working directory and then writes its name.

The ~- extension will be expanded to $OLDPWD before any command is executed and can be passed as an argument for any command, not just cd. It is a later extension in and .

The aforementioned POSIX.1-2008 Shell & Utilities standard also has an elaborate explanation of Tilde Expansion. The wording is very specific to allow for ~- as undefined behaviour within the scope of the standard, where ~[name] refers to $HOME if [name] is the empty string, or the home directory of user name if name is a valid user name.

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.