Shell Navigation 101

There's more than just 'cd'

 ยท 6 min
NASA ELECTRONIC ASSOCIATES ELECTRONIC COMPUTER - Glenn Research Center History, 1956

Navigating the shell is an indispensable skill. While GUIs might be more intuitive in some ways than the command line, they lack the speed and raw power a shell offers. Therefore, efficient shell navigation that goes beyond just cd goes a long way.

This short guide will recap the basics before going beyond them, looking at more sophisticated navigation commands.


The Basics of Shell Navigation

Let’s start with the essentials, focusing on probably the most familiar commands.

Going Places (cd)

The cd command is a shell built-in that, who would have guessed, changes the current working directory. It’s likely one of the first commands you ever learned and is ingrained into muscle memory, but it has more to offer than meets the eye.

It supports both absolute and relative paths:

shell
# ABSOLUTE
$ cd /usr/local/bin

# RELATIVE
$ cd ../logs

The double-dots (..) represent the parent directory. There’s also the single-dot (.) to represent the current working directory, but we don’t need to include it. The following calls are equal:

shell
$ cd ./logs

# IS EQUAL TO

$ cd logs

Without any path after cd, the current working directory is changed to the content of the HOME shell variable, the home directory.

The first underutilized and quite handy feature of the cd is using a dash (-) in place of a path, which returns to the previous directory:

shell
# NAVIGATE TO HOME DIRECTORY
$ cd 

# NAVIGATE FAR AWAY
$ cd /usr/local/bin

# JUMP BACK TO PREVIOS DIRECTORy (HOME)
$ cd -

And, finally, the most apparent problem with cd is not escaping a path correctly:

shell
$ cd "My Documents"

Where am I? (pwd)

The pwd command outputs the absolute path of the current working directory. It’s an invaluable tool when working deep within nested directories or getting the current directory in scripts, etc.

shell
$ pwd
/home/ben/articles

It has two options:

  • -L/--logical: Uses the value from $PWD even if it contains symlinks
  • -P/--physical: Avoid symlinks in the output

By default, -P is assumed.

As pwd is also a shell built-in, the actual implementation might depend on the shell you’re using. But the overall functionality won’t differ.


Beyond the Basics: The Directory Stack

Navigating complex projects often requires frequent directory switching. Instead of relying solely on cd in a repetitive fashion, we can use directory stacks instead.

What is the Directory Stack?

Directory stacks are, well, a stack structure, meaning they follow the last in, first out principle. It’s a list where we can “push” directories on top and “pop” them off to the previous one as needed.

The pushd command stores the current working directory and, navigates to the new one, and outputs the stack:

shell
$ cd
$ pushd /user/local/bin
/usr/local/bin ~

The first listed directory is the current directory, and the previous one, our home in the form of ~ (tilde), is the next one on the stack.

Removing the top directory from the stack and navigating to the previous one is done with the popd command:

shell
$ cd
$ pushd /etc
/etc ~

$ pushd /var/logs
/var/logs /etc ~

# GOING BACK TO /etc
$ popd
/etc ~

Managing the Directory Stack

The dirs command complements the other directory stack management utilities.

At its core, it displays the current directory stack, a list of directories we navigated using pushd and popd. By default, the current working directory is always the topmost entry in the stack:

shell
$ dirs
~

The output format is controllable via multiple options:

  • -l: Expands ~ (tilde) to the actual path
  • -p: Prints every stack entry on a separate line
  • -c: Clear the stack
  • -v: Also prints one per line but prefixes the index

The last option (printing with index) is useful for pushd and popd, as they allow to modify the stack:

shell
$ dirs -lv
0   /boot
1   /var/log
2   /etc
3   /home/ben

# ROTATE FORWARD
$ pushd +2
/var/log /etc ~ /boot

By using +2 as the option for push, the second entry of the stack was moved to the top and became the current working directory.

The same is possible in reverse, by using - (minus) and the position from the bottom of the stack.

These calls do not add a new directory but reorder the whole stack.

It’s also possible to remove specific entries from the stack by using the same principle with popd:

shell
$ dirs -lv
0   /boot
1   /var/log
2   /etc
3   /home/ben

# ROTATE FORWARD
$ popd +2
/boot /etc ~

And, of course, the opposite direction is done by using - (minus).

As the directory is removed, the current working directory hasn’t been changed.


Where Are My Things: CDPATH

CDPATH is a shell variable that defines search paths for the cd command. Instead of typing full paths, you can quickly jump to directories in CDPATH.

Settings CDPATH

The simplest way to modify it is exporting it in the shell’s rc-file, for example, the ~/.bashrc:

shell
CDPATH=.:~:/mnt/data

This particular CDPATH looks in the following locations in order:

  1. .: The current directory
  2. ~: The home directory
  3. /mnt/data: A specific folder

Now, we can cd to any folder in one of the paths wherever we are.

For example, if I have a folder called code in my home directory, cd code works from any location. Unless there’s a code directory in the current working directory, which has precedence.

Be aware that quoting the value didn’t work for me. I’m not sure if this is a shell-specific thing, and I didn’t find any information about it.

Best Practices and Caveats

Modifying CDPATH sounds intriguing at first, but we have to be aware of a few things:

  • Always include . (dot) to prioritize the current directory
  • Don’t overdo it to avoid unexpected matches
  • Don’t export the variable

The last advice is that it is imperative to not pollute the environment and only affect interactive shells.

This is especially important if cd is used in shell scripts. To be absolutely sure, calling unset CDPATH might be a good idea to start from a clean slate.

Another way to circumvent CDPATH is to use absolute paths, even for seemingly relative ones:

shell
# USES CDPATH
$ cd code

# IGNORES CDPATH
$ cd ./code

However, this is a mere band-aid, not a sensible solution to the problem. To be honest, I’d recommend leaving CDPATH alone, as it might break a lot of things quite subtly. Maybe not on your machine, but when you share scripts with others.

As an alternative, I suggest using aliases for directories you frequently visit.

shell
alias ce='cd ~/code/elipse'

This approach’s main advantage is choosing an alias name to your liking, independently of the actual directory name.


Conclusion

The commands dirs, pushd, and popd are handy tools for managing directory stacks, especially when paired with their options. By mastering these commands and their nuances, you can significantly improve your efficiency in navigating the shell.

Whether you’re working on large projects, switching between directories for system administration, or just trying to keep your terminal workspace organized, these tools will save you time and effort.


Resources