Module 1: The Terminal and the File System

7. Navigating and inspecting: pwd, ls, and cd

Description

By the end of this lesson you will be able to confirm at any moment which directory you are standing in with pwd, read a full ls -l line column by column without guessing a single field, filter and sort that output with -a, -h, -t, -S, and -R depending on what you need to see, move around the entire tree with cd using absolute paths, relative paths, ~, and -, see a folder's entire structure at a glance with tree, and precisely diagnose the three error messages that waste the most time for someone just starting out.

This is not ceremony. You inherit the laptop of someone who left the company, or you SSH into a server you have never seen, and the first real question is not "what command do I run?" — it is "where am I standing and what is around me?" An experienced developer does not memorize the structure of every new project: they reconstruct it in seconds with pwd, ls, and cd, before touching a single line of code. Without that habit, every unfamiliar folder feels like walking into a dark room.

Connection to the module: the previous lesson gave you the map — a single tree rooted at /, absolute and relative paths, the shortcuts ., .., ~, and -. Today you get the three commands that use that map hundreds of times a day: pwd tells you where you are, ls shows you what is there, cd moves you. The next lesson does not teach anything new: it gives you twelve real questions about your own machine that you can only answer with exactly these three commands.


pwd: the question you answer before acting

Before walking in any direction inside a mall you do not know, you look at the map on the wall and find the dot that says "you are here." Without that dot, whatever route you pick is a gamble. The same thing happens in the terminal: before deleting something, moving something, or running a script that "should" be there, you confirm where you are standing. pwd (print working directory) answers that one question, with no frills.

You already saw in lesson 2 that the prompt normally shows "where you are standing" as part of its information. But the prompt is configurable: many setups shorten it, show only the last stretch of the path, or drop it entirely to save space. pwd does not depend on any configuration — it always gives you the full absolute path, unabbreviated, exactly as the system understands it right now. When something does not add up, pwd is the source of truth, not the prompt.

Worked example

pwd

What to expect (your actual path depends on your username and where you are standing; the shape is what matters):

/Users/alex

No arguments, no options, no ambiguity: that is the absolute path of your current directory at this instant. You are going to type pwd dozens of times per session, almost always before a command that changes something — it is the habit that separates someone who acts with certainty from someone who acts blind and hopes for the best.


ls -l: the same label, seven fields, on every line

Think about a package that arrives at your door. No matter what is inside the box — a book, clothes, a spare part — the label stuck on the outside always carries the same fields, in the same order: who sent it, how much it weighs, when it shipped, who it is addressed to. ls -l (long format) prints exactly that kind of label, one per line, for every file or folder: seven fixed fields, always in the same order, whether what they describe is a one-line text file or a folder with thousands of files inside.

You are going to build a practice folder to read that label at a relaxed pace. You already used mkdir in lesson 4 to create a folder; touch is new, and it does something just as simple: it creates an empty file (you will study both in depth in module 2; for now, what they do is enough):

cd ~
mkdir lab-nav
cd lab-nav
mkdir photos
mkdir photos/vacation
touch notes.txt
touch .env
touch photos/vacation/beach.jpg

None of those lines print anything — no output means success, as you have already seen with other commands. Now ask for the long format:

ls -la

What to expect (the owner, the group, the size in blocks, and the exact date are going to vary on your machine; the shape of each line will not):

total 0
drwxr-xr-x  5 alex  staff  160 Jul 21 10:23 .
drwx------ 20 alex  staff  640 Jul 21 10:23 ..
-rw-r--r--  1 alex  staff    0 Jul 21 10:23 .env
-rw-r--r--  1 alex  staff    0 Jul 21 10:23 notes.txt
drwxr-xr-x  3 alex  staff   96 Jul 21 10:23 photos

Take the notes.txt line and break down each field, left to right:

-rw-r--r--  1  alex  staff  0  Jul 21 10:23  notes.txt
│            │  │     │     │  │              └── name
│            │  │     │     │  └── last modification date
│            │  │     │     └── size in bytes
│            │  │     └── owning group
│            │  └── owning user
│            └── number of links
└── type + permissions (10 characters)
  • Type + permissions (-rw-r--r--, ten characters): the first character is the one you care about today — - for a regular file, d for a directory, l for a symbolic link. The remaining nine characters describe full permissions for three different user classes, and you are only going to decode them letter by letter in module 4. For now, just keep the first character: it tells you at a glance whether something is a file or a folder, without having to guess from the name.
  • Number of links: how many references point to that same data on disk. For a regular file it is almost always 1. For a directory, on a traditional filesystem (the kind running on most Linux servers) it starts at 2 — itself, plus its own . entry — and goes up by one for every direct subfolder it contains. If your machine is a Mac with APFS (the default filesystem since 2017), you are going to see different behavior: the number goes up with any new entry, whether a file or a subfolder, not just subfolders. If the number does not match the classic rule on your Mac, it is not a mistake on your part: it is a real design difference between filesystems, not something you need to resolve today.
  • Owner and group (alex staff): who owns the file and which group it belongs to. You are going to understand the full mechanism behind these two fields — why they exist, who can change them — in module 4; today just reading them is enough.
  • Size: the file's weight in bytes. notes.txt weighs 0 because touch created it empty.
  • Date: the last time the file's content was modified — not when it was created. If the file is more than roughly six months old, ls switches the format: instead of the time of day, it shows the year, because at that distance the exact time stops being the useful piece of data.
  • Name: the last field, unambiguous. If the entry were a symbolic link, here you would also see an arrow (-> target) pointing to what that link represents.

One more detail: the first line, total 0, is not the sum of the sizes you see below — it is the number of disk blocks reserved for the directory itself, in a unit that depends on the filesystem. Do not worry if the numbers do not add up by hand; it is not meant to be summed with the size column's bytes.


ls modifiers: which rows you see and in what order

You already know how to read a full row. The following modifiers do not change what each column means — they change which rows show up and in what order, depending on what you are looking for at that moment.

FlagWhat it does
-aIncludes hidden files (the ones starting with .), including . and ..
-hCombined with -l, shows size in human-readable units (K, M, G) instead of raw bytes
-tSorts by modification date, most recent to oldest
-SSorts by size, heaviest to lightest
-RRecurses into every subfolder, instead of showing only the current level

Worked example

-a, hidden files. You already used it above without naming it separately: .env showed up in your lab-nav listing because you added -a. Without it, a plain ls -l would have skipped it entirely — exactly the behavior you saw in the previous lesson with the leading dot.

-t, sorting by date. Run this inside lab-nav:

ls -lt

What to expect (the exact order of your files is going to depend on exactly when you ran each touch; what matters is that the most recent one comes first):

total 0
-rw-r--r--  1 alex  staff    0 Jul 21 10:23 notes.txt
drwxr-xr-x  3 alex  staff   96 Jul 21 10:23 photos

notes.txt comes first because you created it with touch after the last change inside photos (adding the vacation subfolder) — -t does not care when each thing was created, only when each entry was last modified.

(-a is not in this combo, so .env does not show up — remember modifiers combine freely: ls -lat gives you both at once.) This is the question -t answers: "what did I touch most recently in this folder?" — the first one you ask yourself when something changed and you do not remember what.

-S and -h together, on a folder with real content. Your practice folder has nothing but empty files, so sorting by size there does not say much. Try it on your own home folder, where there is real variety:

cd ~
ls -lhS

What to expect (your list is going to be completely different from anyone else's — what matters is the criterion: largest to smallest, with the size already converted to K, M, or G):

total 48
-rw-r--r--@  1 alex  staff    12K Jul 21 09:47 report.pdf
drwx------@  8 alex  staff   2.1K Jul 20 19:14 Downloads
drwx------@  5 alex  staff   1.4K Jul 15 08:02 Documents
drwxr-xr-x@  5 alex  staff   160B Jul 21 10:23 lab-nav

This combination — -lhS — is exactly the one you run when the disk is filling up and you need to know, without opening anything, what is taking up the most space. Without -h you would be reading a raw byte count (2148, 1434) instead of 2.1K, 1.4K — technically the same, but much slower to compare at a glance.

-R, recursive. Go back to lab-nav and ask for the whole tree at once:

cd ~/lab-nav
ls -laR

What to expect:

.:
total 0
drwxr-xr-x  5 alex  staff  160 Jul 21 10:23 .
drwx------ 20 alex  staff  640 Jul 21 10:23 ..
-rw-r--r--  1 alex  staff    0 Jul 21 10:23 .env
-rw-r--r--  1 alex  staff    0 Jul 21 10:23 notes.txt
drwxr-xr-x  3 alex  staff   96 Jul 21 10:23 photos

./photos:
total 0
drwxr-xr-x  3 alex  staff   96 Jul 21 10:23 .
drwxr-xr-x  5 alex  staff  160 Jul 21 10:23 ..
drwxr-xr-x  3 alex  staff   96 Jul 21 10:23 vacation

./photos/vacation:
total 0
drwxr-xr-x  3 alex  staff   96 Jul 21 10:23 .
drwxr-xr-x  3 alex  staff   96 Jul 21 10:23 ..
-rw-r--r--  1 alex  staff    0 Jul 21 10:23 beach.jpg

-R repeats the same ls -la format once for every folder it finds, going down level by level, with a heading (./photos:) that tells you which one you are standing in for each block. It is exhaustive — and, as you are about to see, there is a more comfortable way to look at exactly this same information.


tree: the whole tree, not one branch at a time

Up to now, every time you wanted to see what was further down, you had to descend with cd or ask for -R, which hands you the content back in blocks separated by folder. If the filesystem is a tree — the previous lesson's central idea — so far you could only look at one branch at a time. tree draws the whole tree, from the root you point it at down to the last leaf, in a single piece of text art.

Worked example

cd ~/lab-nav
tree

What to expect, if tree is installed:

.
├── notes.txt
└── photos
    └── vacation
        └── beach.jpg

3 directories, 2 files

(By default, tree does not show hidden files — that is why .env does not appear here; add -a to include them, same as with ls.) It is the same information you already saw with ls -laR, but drawn as a hierarchy instead of listed in plain-text blocks — faster to read when what you want is the whole shape of the tree, not the line-by-line detail of each file.

What to expect if tree is not installed (the most common case: it does not come preinstalled by default on either macOS or most Linux distributions):

zsh: command not found: tree

You already know this message from lesson 4: the shell searched for a program with that exact name in your PATH and did not find it. Two paths, depending on the machine you are on:

  • If you can install packages (your own laptop): on macOS with Homebrew, brew install tree; on Ubuntu, Debian, or inside WSL, sudo apt install tree; on Fedora, sudo dnf install tree.
  • If you cannot (someone else's server, a container with no install access): you already have an exact substitute, with nothing to install — ls -laR, which you just used. It does not draw as pretty a tree, but it gives you exactly the same complete information.

Common mistakes

1. Assuming a relative path resolves from where the file "should" be, not from your current directory (conceptual). What happens: you type cd photos while standing in your home instead of inside lab-nav, and the terminal responds:

-bash: cd: photos: No such file or directory

You already know this message from lesson 4 — it means the program (here, cd) did start, but one of its arguments does not exist where it looked for it. Why it happens: someone just starting out assumes a relative path like photos resolves "from where the file lives" or "from home," when in reality it always resolves from your actual current directory, no exceptions — and that directory might not be the one you assume if you lost track of where you are. It can also be a case-sensitivity trap: if you typed cd Photos on Linux while having a lowercase photos folder, the system distinguishes uppercase from lowercase — the exact trap you saw in the previous lesson — and the result is the same error. How to spot it: the message specifically names the path it did not find, not the command. How to fix it: pwd first, always, to confirm where that relative path is being resolved from before typing it; if the name looks right, check uppercase and lowercase letter by letter.

2. Trying to cd into a file, not a folder. What happens: you type cd notes.txt (a real file inside lab-nav) and the terminal responds something like:

-bash: cd: notes.txt: Not a directory

Why it happens: cd only knows how to move you between directories; notes.txt exists, and that is why the message is not "No such file or directory" — it found it — but it is not a place you can enter, it is a file with content. The typical confusion is assuming any name that shows up in an ls is a valid destination for cd, without checking the first character of ls -l (- for a file versus d for a directory) that you already learned above. How to spot it: the message explicitly says "Not a directory" — different from "No such file or directory" — so the name does exist, it is just not the kind of entry cd needs. How to fix it: run ls -l on that name and check the first character before trying to enter it; if it is -, you need a different command (to read it, not to enter it) — one you are going to learn in module 2.

3. Permission denied when trying to enter a folder that does not belong to you. What happens: on Linux or inside WSL, try:

cd /root

On macOS, the equivalent is:

cd /private/var/root

What to expect on Linux or WSL, if you are not using the root account (the normal case day to day):

-bash: cd: /root: Permission denied

And on macOS, the same kind of message about the other path:

-bash: cd: /private/var/root: Permission denied

Why it happens: that folder is the personal directory of the root account, and its permissions are deliberately configured so no one else can enter, not even to look at what it contains. It is not a corrupted file or a bug in your terminal: the system denied you access because it was configured that way. The full mechanism behind this — who decides, how it is read, how it is changed — is exactly module 4's topic; today it is enough to recognize the message and know it is not fixed with a different command, but with permissions you do not have today. How to spot it: the message ends in "Permission denied," neither "No such file or directory" (the path exists) nor "Not a directory" (it is in fact a folder). How to fix it: day to day, you do not fix it — either you do not need to go in there, or you need privileges you are going to learn to request with judgment later in the guide.


Exercises

1. Read the full label. You have this line, taken from an ls -l:

-rw-r--r--  1 mia  developers  4096  Mar 15 2025  budget.xlsx

Without touching the full permissions column yet (that is module 4): is it a file or a folder? Who is the owner? Which group does it belong to? How many bytes does it weigh? When was it last modified?

See solution

It is a file (the first character is -, not d). The owner is mia. The group is developers. It weighs 4096 bytes. It was last modified on Mar 15 2025 — and since the date shows the year instead of a time, that is a clue that more than roughly six months passed between that modification and today.

Why it works: each of these pieces of data lives in a fixed position on the line, no matter the file — the same order you saw with notes.txt in the worked example: type+permissions, links, owner, group, size, date, name.

2. The shortest path. You are standing in ~/lab-nav/photos/vacation. Using only what you learned in this lesson (relative paths, .., ~, -), write the shortest sequence of cd commands to reach your personal directory (~) and then go back exactly to ~/lab-nav/photos/vacation without typing the full path the second time.

See solution
cd ~
cd -

Why it works: cd ~ takes you to home in a single step, with no need for any .. (much shorter than cd ../../..). Once there, cd - takes you back exactly to the directory you just left — $OLDPWD — without typing ~/lab-nav/photos/vacation again.

3. Pick the right flag. For each task, say which combination of ls flags you would use: (a) see, in your Downloads folder, which file is heaviest, with the size in human-readable units; (b) confirm whether a configuration folder has a hidden file you had not noticed; (c) see a project's full structure, folder by folder, without entering each one with cd.

See solution

(a) ls -lhS-S sorts heaviest to lightest, -h converts bytes to K/M/G so it reads at a glance. (b) ls -la-a also shows entries starting with .. (c) ls -laR (or tree, if installed) — -R recurses into every subfolder in a single command.

Why it works: each flag answers a different question — what weighs the most, what is hidden, what is further down — and they combine freely because each one filters or sorts independently.

4. Diagnose without running anything. A coworker sends you these three error messages, with no other context, and asks what causes each one: (a) cd: /home/user/project: No such file or directory; (b) cd: report.pdf: Not a directory; (c) cd: /etc/ssl/private: Permission denied. Explain each one in a sentence, without running anything yet.

See solution

(a) The path /home/user/project does not exist where the system looked for it — probably a misspelled name, a folder that moved, or a case-sensitivity difference. (b) report.pdf does exist, but it is a file, not a folder — cd cannot enter something with no navigable content. (c) /etc/ssl/private exists and is a folder, but its permissions are configured so the current account cannot enter — something deliberate, not a broken file (for good reason: on many systems that is where server private keys live).

Why it works: the three messages are literally different from each other because they describe three different causes — does not exist, is not a folder, exists but not authorized — and that textual distinction is the only clue you need before deciding what to fix.


Summary and next step

Before moving on you should be able to:

  • confirm with pwd which directory you are standing in, without depending on what your prompt shows;
  • read any ls -l line field by field — type, links, owner, group, size, date, name — and combine -a, -h, -t, -S, and -R depending on what you need to see;
  • move around with cd using a relative path, an absolute path, ~, and -, without having to think through each step;
  • see a folder's full structure with tree, or with ls -laR if tree is not available;
  • tell apart No such file or directory, Not a directory, and Permission denied just by reading the message, and know which family of cause each one belongs to.

You now have the tools to orient yourself on any machine put in front of you, familiar or not. What you are missing now is not a new command: it is practicing it on your own real machine, with no artificial safety net. That is exactly the project that follows — twelve concrete questions about your own filesystem, each one answered with the exact command that produced it.


Resources