unix-foundations Lesson 2 18 min read

How Do I Navigate Files and Directories?

Building your workspace from the command line — mkdir, cp, mv, and the art of staying organized

Reading: Linux Text: Ch. 2–3, pp. 46–95

After this lesson, you will be able to:

  • Create directories with mkdir and nested structures with mkdir -p
  • Navigate the file system with cd and understand ., .., ~
  • Copy files and directories with cp and cp -r
  • Move and rename files with mv
  • Remove files and directories with rm and rm -r
  • View file contents with cat, less, head, and tail
  • Use wildcards (*, ?, [...]) to match multiple files

Your Workspace Is Empty

You’ve opened your terminal, typed ls, and seen… almost nothing. Maybe a Desktop folder or a Downloads directory. But where do your labs go? Where does your source code live?

In CSCD 210, VS Code handled this for you — you cloned a repo and the file explorer showed everything in a nice tree. But now you need to build that tree yourself, from the command line. And once you’re comfortable doing it, you’ll realize it’s actually faster than clicking through menus.

Let’s build a real workspace, one command at a time.


Creating, Moving, and Organizing

Creating Directories: mkdir

Start in your home directory:

cd ~
pwd

You should see /home/student (or whatever your username is). Now let’s create your course directory:

mkdir cscd240

Verify it exists:

ls

You should see cscd240 in the listing. Now let’s create the directory structure you’ll use all quarter:

mkdir cscd240/labs
mkdir cscd240/labs/lab1
mkdir cscd240/labs/lab2
mkdir cscd240/notes

Or, more efficiently, use the -p flag to create parent directories automatically:

mkdir -p cscd240/labs/lab3/src

This creates lab3 and src inside it, even though lab3 didn’t exist yet. Without -p, that command would fail because lab3 doesn’t exist.

The Trick: mkdir -p never complains if a directory already exists and creates any missing parent directories. Use it whenever you’re setting up nested structures. It’s like Java’s Files.createDirectories() — safe to call even if the path already exists.

Why does this matter?

Lab setup scripts and Makefiles use mkdir -p constantly to create output directories before compilation. If you understand -p, you’ll read those scripts without confusion. If you don’t, you’ll wonder why they don’t just use plain mkdir.

Now let’s move around:

cd cscd240
pwd

Output: /home/student/cscd240. You’re inside your course directory. Let’s go deeper:

cd labs/lab1
pwd

Output: /home/student/cscd240/labs/lab1. Notice we used a relative path — we didn’t start with /.

Now go back up:

cd ..
pwd

Output: /home/student/cscd240/labs. The .. means “parent directory” — one level up.

Go up two levels:

cd ../..
pwd

Output: /home/student. Each .. goes up one level.

Some shortcuts you’ll use constantly:

cd ~            # Go home, no matter where you are
cd              # Same thing — cd with no argument goes home
cd -            # Go back to the PREVIOUS directory (toggle)

Key Insight: cd - is an underappreciated shortcut. If you’re bouncing between two directories — say your source code and your test output — cd - toggles between them instantly. It’s like Alt+Tab for directories.

Check Your Understanding
You're in /home/student/cscd240/labs/lab1. What does cd ../.. do?
A Moves you to /home/student/cscd240
B Moves you to /home/student
C Moves you to /home/student/cscd240/labs
D Gives an error because you can't use .. twice
Answer: A. Each .. moves up one directory level. From lab1, the first .. goes to labs, the second goes to cscd240. If you picked B, you went up three levels instead of two — count the .. segments separated by /.

Creating Files: touch

The touch command creates an empty file (or updates the timestamp of an existing one):

cd ~/cscd240/labs/lab1
touch hello.c
touch Makefile
ls -l

You now have two empty files ready for content. We’ll fill them in when we start writing C code in Series 2.

Viewing Files: cat, less, head, tail

Let’s say you have a file with content. Here are four ways to look at it:

cat hello.c           # Print entire file to screen (good for short files)
less hello.c          # Scrollable viewer (arrow keys, /search, q to quit)
head -5 hello.c       # First 5 lines
tail -3 hello.c       # Last 3 lines

For now, our files are empty, but these commands become essential when you’re looking at source code, compiler output, or log files.

wc (word count) tells you file statistics:

wc hello.c            # Lines, words, bytes
wc -l hello.c         # Just line count

Copying Files: cp

cp hello.c hello_backup.c
ls

Now you have two files. To copy a directory, you need the -r (recursive) flag:

cd ~/cscd240/labs
cp -r lab1 lab1-backup
ls

Without -r, cp refuses to copy directories — it’s a safety feature so you don’t accidentally duplicate entire directory trees.

Moving and Renaming: mv

mv does double duty — it moves files and renames them:

cd ~/cscd240/labs/lab1
mv hello_backup.c hello_v1.c        # Rename
mv hello_v1.c ../                    # Move to parent directory (labs/)
ls
ls ..

Moving a file to a directory moves it there. Giving it a new name renames it. You can do both at once:

mv ../hello_v1.c ./old_hello.c      # Move back and rename in one step

From Java: In CSCD 210, you renamed files by right-clicking in VS Code’s file explorer. In Unix, mv old_name new_name does the same thing — and it works on directories too. There’s no separate “rename” command because moving to the same location with a new name is renaming.

Why does this matter?

Every lab submission starts with organizing files in the right directories. If you mv a file to the wrong place or cp without -r on a directory, you’ll lose work or submit incomplete labs. These commands are muscle memory by Week 3.

Check Your Understanding
You run cp -r lab1 lab1-backup. Which statement is true?
A lab1 is renamed to lab1-backup
B Only the files inside lab1 are copied, not subdirectories
C The command fails because lab1-backup doesn't exist yet
D Both lab1 and lab1-backup exist with identical contents
Answer: D. cp duplicates — the original stays put. The -r flag copies the directory and everything inside it recursively. Without -r, cp refuses to copy directories at all. If you picked A, you're thinking of mv, which moves/renames rather than copying.

Deleting: rm (Handle With Care)

rm old_hello.c          # Delete a file — PERMANENT, no trash can

Common Pitfall: rm is permanent. There is no Recycle Bin, no Undo, no “Are you sure?” (unless you add the -i flag). Before running rm, always: (1) run pwd to confirm your location, (2) run ls to see what’s there, (3) read your command carefully before pressing Enter.

For directories:

rmdir empty_directory    # Only works if directory is empty (safe)
rm -r directory_name     # Delete directory and everything inside (dangerous)

Use rm -i for interactive confirmation:

rm -i some_file.c
# rm: remove regular file 'some_file.c'? y

Wildcards: Pattern Matching

Wildcards let you operate on multiple files at once. The shell expands them before the command runs:

Pattern Matches
* Any number of any characters
? Exactly one character
[abc] One character from the set
[0-9] One character from the range
[!0-9] One character NOT in the range

Examples:

ls *.c                  # All .c files
ls lab?.c               # lab1.c, lab2.c, but NOT lab10.c
ls lab[123].c           # Only lab1.c, lab2.c, lab3.c
ls *.[ch]               # All .c and .h files

From Java: Java’s File.listFiles(filter) requires writing a FilenameFilter or lambda. Unix wildcards do the same filtering with a few characters. The shell handles the expansion — the command itself never sees the wildcard characters.

Check Your Understanding
A directory contains lab1.c, lab2.c, lab10.c, and lab2.h. Which files does ls lab?.c match?
A lab1.c, lab2.c, and lab10.c
B All four files
C lab1.c and lab2.c only
D lab2.c and lab2.h
Answer: C. The ? wildcard matches exactly one character — so lab?.c matches lab + one character + .c. That gives you lab1.c and lab2.c. It does NOT match lab10.c because 10 is two characters. It doesn't match lab2.h because the extension is .h, not .c.

Seeing the Full Picture: ls -R and tree

After building your workspace, verify the structure:

cd ~/cscd240
ls -R

The -R flag lists directories recursively — every subdirectory and its contents. If tree is installed, it gives an even prettier view:

tree
cscd240/
├── labs/
│   ├── lab1/
│   │   ├── hello.c
│   │   └── Makefile
│   ├── lab2/
│   └── lab3/
│       └── src/
└── notes/
Quick Check: What's the difference between cp and mv?

cp creates a duplicate — the original stays where it is. mv moves (or renames) — the original is gone from its old location. For directories, cp requires the -r flag; mv does not.

Quick Check: Why does mkdir cscd240/labs/lab3/src fail if lab3 doesn't exist?

By default, mkdir only creates the final directory in the path. If any parent directory is missing, it fails. The -p flag tells mkdir to create all missing parent directories along the way.

Quick Check: What does the wildcard lab[!0-9].c match?

It matches files like labA.c, labx.c, or lab_.c — any file named lab followed by exactly one character that is NOT a digit (0–9), followed by .c.

Naming Conventions

The Trick: Follow these rules for filenames and you’ll avoid most headaches:

  • No spaces — use hyphens (my-file.c) or underscores (my_file.c)
  • Lowercase — Unix is case-sensitive; Hello.c, hello.c, and HELLO.C are three different files
  • Meaningful extensions.c for C source, .h for headers, .o for object files
  • Bad: Lab 1 Final (2).c — spaces, uppercase, parentheses
  • Good: lab1-final.c

You’ve Built Your Workshop

You can now create, navigate, copy, move, and delete files and directories from the command line. That’s the foundation for everything else this quarter — every lab starts with setting up a directory structure, and every project involves managing source files.

But right now, when you get stuck, where do you go for help? You can’t hover over a command in VS Code to get a tooltip. In the next lesson, you’ll learn about Unix’s built-in documentation system — man pages — and the command history features that make the shell feel less like typing into the void.

Big Picture: The directory structure you just built by hand is exactly what git clone creates automatically when you clone a lab repo. Understanding the structure means you’ll know where things are when something goes wrong — and in C development, knowing where your files are is half the battle.