The Filesystem and Navigation · about 35 min

Moving around: cd, pwd and paths

Why this matters

Working in the shell means constantly moving between directories: into a project, over to the logs, back home. Being quick and exact with paths saves time, and more importantly it prevents the classic accident of running a command in the wrong place. Scripts also depend on knowing where they are: a script that assumes it is started from one particular directory breaks the first time someone runs it from another.

pwd: where am I?

Every process has a current working directory (CWD), kept by the kernel. Relative paths are resolved from it. pwd prints the shell's:

$ pwd
/work

The prompt in this sandbox also shows it (learner@sandbox:/work$), and abbreviates your home directory as ~.

cd: change directory

cd takes one path, absolute or relative, and makes it the shell's new current directory:

$ cd /etc
$ pwd
/etc
$ cd apt
$ pwd
/etc/apt
$ cd ..
$ pwd
/etc
$ cd ../usr/share
$ pwd
/usr/share

Remember from lesson 2 of module 1 that cd is a builtin. It calls the chdir system call in the shell's own process, so the change sticks for every command you run afterwards (each one is forked from the shell and inherits the new directory).

Shortcuts

CommandGoes to
cdyour home directory (the value of $HOME)
cd ~the same: the shell replaces ~ with your home directory
cd ~/notesnotes inside your home
cd -the previous directory (and prints it)
cd ..the parent directory
cd /the root
$ cd /var/log
$ cd /etc
$ cd -
/var/log
$ cd -
/etc
$ cd
$ pwd
/home/learner

cd - is wonderful for bouncing between two places. It works because bash remembers the previous directory in the variable OLDPWD.

The tilde is expanded by bash

~ is not a directory name the kernel knows about. bash replaces a ~ at the start of a word with the value of $HOME before running the command:

$ echo ~
/home/learner
$ echo ~/notes
/home/learner/notes
$ echo "~"
~

Inside quotes it is left alone. This is your first example of expansion: bash rewriting the line before running it. Module 7 is all about this.

Relative paths, carefully

From /work/maze/north/cellar:

/work
└── maze
    ├── north
    │   ├── attic       ../attic         (sibling: up one, down one)
    │   └── cellar      .                (here)
    └── east            ../../east       (up two, down one)

. means "this directory". You rarely need it in cd, but it matters in two places: running a program from the current directory (./script.sh, because . is not in PATH), and making a name that starts with - look less like an option (./-file).

. and .. are real entries stored in every directory, pointing at the directory itself and its parent. The kernel follows them like any other name.

There is one subtlety. When a path goes through a symbolic link (a shortcut to another directory, covered in module 3), bash's cd normally keeps track of the path as you typed it (the logical path), so cd .. takes you back the way you came. The kernel's view (the physical path) may be different. pwd -P shows the physical one:

$ cd /bin
$ pwd
/bin
$ pwd -P
/usr/bin

On this Debian system /bin is a symbolic link to /usr/bin. For everyday work the logical view is what you want; just know the two exist when paths look surprising.

Listing as you go: ls and tree

ls with no argument lists the current directory; give it a path to list somewhere else without moving:

$ ls /work/maze
east  north  south  west
$ ls maze/north
attic  cellar

tree draws a whole subtree, which is great for getting your bearings:

$ tree maze
maze
├── east
│   └── empty.txt
├── north
│   ├── attic
│   │   └── box.txt
│   └── cellar
...

tree -d shows only directories, tree -L 2 only two levels deep.

Use Tab completion constantly: cd ma<Tab> becomes cd maze/, and cd maze/<Tab><Tab> lists the choices.

Paths in scripts

A script inherits the current directory of whoever started it. So a script that does cat data.txt works only if it is run from the directory containing data.txt. Two habits:

A cd inside a script changes the directory of the script's process only. When the script ends, your interactive shell is exactly where it was, for the same fork-and-exec reason that cd has to be a builtin.

Common mistakes

Recap

Going further: try pushd /etc, pushd /var/log, dirs, popd: a stack of directories built into bash. help pushd explains it.

Exercises

Exercise 1 · quiz

Resolve a relative path

Your current directory is /home/ana/projects/web. You run:

cd ../../../bob/./docs

Assuming all those directories exist, what does pwd print afterwards? Type the absolute path.

Answer

Step by step from /home/ana/projects/web: .. gives /home/ana/projects, .. gives /home/ana, .. gives /home, then bob gives /home/bob, . stays there, and docs gives /home/bob/docs. Each .. removes one component from the right; . changes nothing.

Exercise 2 · task in a shell

Find the note

The workspace contains a directory tree called maze. Somewhere inside it is a file named note.txt. Use cd, ls and pwd to find it (no find yet: explore by hand, it is a small tree; tree maze from /work shows you the whole thing if you get lost).

When you find it, read it with cat. It tells you what to do next.

Hint 1

cd maze, then ls, then cd into a subdirectory and ls again. cd .. goes back up.

Hint 2

It is in the south. Once there, pwd > /work/found.txt writes the absolute path into the file.

Reference solution
cd maze/south/garden/shed && pwd > /work/found.txt

Exercise 3 · task in a shell

Place files with relative paths

Still in the maze: without leaving /work/maze/north/cellar once you are there, use relative paths to

  1. create a file /work/maze/north/cellar/here.txt containing cellar,
  2. create a file /work/maze/north/attic/up.txt containing attic (the attic is a sibling of the cellar: go up with ..),
  3. create a file /work/maze/east/far.txt containing east.

Start with cd /work/maze/north/cellar and then only use paths that do not begin with /. (The check can only see the files, so this part is on your honor. It is the point of the exercise.)

Hint 1

From the cellar, .. is /work/maze/north, so the attic is ../attic.

Hint 2

echo attic > ../attic/up.txt and echo east > ../../east/far.txt

Reference solution
cd maze/north/cellar
echo cellar > here.txt
echo attic > ../attic/up.txt
echo east > ../../east/far.txt

Exercise 4 · write code

A scripted tour

Your script starts in a directory containing docs/guides and src/lib (they are created for you). Write a script that:

  1. changes into docs/guides and prints the current directory with pwd,
  2. moves to src/lib using a relative path from there (two levels up, then down) and prints pwd again,
  3. runs cd - (which prints the directory it switches to by itself),
  4. runs cd with no argument at all, then prints pwd.

Expected output, when the starting directory is /work:

/work/docs/guides
/work/src/lib
/work/docs/guides
/home/learner

File: script.sh

Starting point

cd docs/guides
pwd

Example

expected output

/work/docs/guides
/work/src/lib
/work/docs/guides
/home/learner
Hint 1

From /work/docs/guides, ../.. is /work.

Hint 2

cd ../../src/lib then pwd; cd - prints by itself; plain cd goes to $HOME.

Reference solution
cd docs/guides
pwd
cd ../../src/lib
pwd
cd -
cd
pwd