What the shell actually is
When you open Terminal.app on your Mac, you're looking at a text box that talks to a program called a shell. The shell is not the terminal window โ it's what runs inside it. The terminal is the window; the shell is the interpreter reading what you type and deciding what to run.
macOS ships with zsh (the Z shell) as the default. Zsh is a Unix shell โ a direct descendant of the same family as bash and sh. The commands you learn here work on your Mac and on remote Linux servers, because they're all speaking the same underlying language: POSIX Unix. This is the foundational vocabulary of every developer tool you'll ever use.
Compare this to Finder. Finder and the shell are looking at exactly the same files on the same disk โ different interfaces to the same filesystem. Finder is point-and-click; the shell is type-and-run. As a developer you'll use both, but everything that involves automation, scripts, remote servers, package managers, and developer tools happens through the shell. Finder can't reach those places.
The reference book for this module (The Linux Command Line) is written for GNU/Linux. macOS uses BSD userland tools โ same commands, slightly different flags in a few places. This lesson flags the differences as they come up. The portable forms taught here work on both.
Where am I? Moving around
The shell always has a current directory โ the folder it considers "here." Every command you run acts relative to that location unless you say otherwise. Three commands cover everything you need to navigate:
pwd โ print working directory
pwd tells you where you are right now. Run it whenever you're disoriented:
$ pwd
/Users/john/projects/demo
That output is an absolute path โ it starts from / (the root of the entire filesystem) and spells out every directory along the way. Every file on your Mac has exactly one absolute path.
ls โ list directory contents
ls shows what's in the current directory. On its own it gives you a quick list. Add flags to see more detail:
$ ls # basic list
$ ls -l # long format: permissions, owner, size, date
$ ls -a # show hidden files (names starting with .)
$ ls -lah # long + all + human-readable sizes โ these combine
$ ls -G # coloured output on macOS (BSD ls)
GNU/Linux uses ls --color. macOS ships with BSD ls, which uses ls -G. If you copy a Linux alias like alias ls='ls --color', it will break on your Mac. The macOS equivalent is alias ls='ls -G'. This is the most common "works on Linux, not on Mac" trip-up for shell newcomers.
cd โ change directory
cd moves you to another directory. You'll use a handful of shorthand forms constantly:
$ cd /Users/john/projects # absolute path โ works from anywhere
$ cd projects # relative โ works only if projects/ is inside the current dir
$ cd .. # go up one level to the parent directory
$ cd ~ # go to your home directory (/Users/john on macOS)
$ cd - # go back to wherever you just were (toggle)
$ cd # no argument โ same as cd ~
The tilde (~) is a shell shorthand for your home directory. You'll see it everywhere โ ~/.zshrc means /Users/john/.zshrc. It's not a real directory name; the shell expands it before running the command.
Looking at files
Before reaching for a text editor, the shell gives you several commands for reading files directly from the terminal โ essential on remote servers where you have no GUI at all.
cat โ print the whole file
$ cat notes.txt
cat dumps the entire file to the terminal in one go. Good for short files. For anything longer than a screenful, use less โ cat on a large file just scrolls past everything you wanted to read.
less โ page through a file
$ less server.log
less opens the file in a scrollable viewer. Controls: arrow keys (or j/k) to scroll, q to quit, /word to search (then n for next match, N for previous). Unlike cat, less doesn't load the whole file into memory first โ it handles gigabyte log files without complaint.
head and tail โ read the beginning or end
$ head config.yaml # first 10 lines (default)
$ head -n 20 config.yaml # first 20 lines
$ tail -n 50 app.log # last 50 lines
$ tail -f app.log # follow: keep printing new lines as they arrive
tail -f is one of the most practical debugging tools you'll use โ run it in a terminal while your app is running to watch log lines appear in real time.
file โ identify what something actually is
$ file mystery_binary
mystery_binary: Mach-O 64-bit executable arm64
$ file photo.jpg
photo.jpg: JPEG image data, JFIF standard 1.01
file inspects the actual bytes of a file (not just its name) and reports what it is. Useful when you find a file with no extension, or an extension that doesn't match the contents.
Absolute vs relative paths
This is the single concept that unlocks everything else in the shell. Once you have this, mysterious "No such file or directory" errors stop being mysterious.
Absolute path: starts with /. Always means the same thing, regardless of where you currently are.
Relative path: starts with a directory name, . (current dir), or .. (parent dir). Means something relative to your current directory.
# Assume you are in /Users/john/
$ cat Documents/notes.txt # relative: looks inside ./Documents/
$ cat /Users/john/Documents/notes.txt # absolute: same file, always works
$ ls ../shared/ # relative: go up one level, then into shared/
$ ./script.sh # relative: run script.sh right here
If you try to run a script by typing just script.sh, the shell won't find it โ even if it's sitting right in front of you. The shell doesn't look in the current directory by default (that's a security feature). You need to be explicit: ./script.sh means "run script.sh from this directory." This surprises everyone the first time.
The most common beginner error: getting "No such file or directory" and staring at it in confusion. Fix: run pwd first. You're probably not where you think you are.
Making and moving things
Five commands cover almost all file management you'll ever do from the shell:
mkdir โ make a directory
$ mkdir projects # create a single directory
$ mkdir -p projects/demo/src # create the whole tree at once
Always use -p. Without it, mkdir fails if any parent directory in the path doesn't exist yet. With -p, it creates whatever is missing, and doesn't error if the directory already exists. It's strictly better in every case.
cp โ copy
$ cp notes.txt notes_backup.txt # copy a file
$ cp -r src/ src_backup/ # copy a directory (-r = recursive)
For directories you must pass -r. Without it, cp refuses to copy a directory and gives you an error.
mv โ move and rename
$ mv old_name.txt new_name.txt # rename a file
$ mv report.txt ~/Documents/ # move to another directory
$ mv src/ ~/projects/myapp/ # move a whole directory
In the shell, rename and move are the same operation. There's no separate rename command โ mv does both.
rm โ remove
$ rm file.txt # delete a file
$ rm -r old_project/ # delete a directory and everything inside it
Deleting a file via Finder sends it to the Trash where you can recover it. rm does not. Files deleted with rm are gone immediately. There is no undo, no confirmation prompt by default, no recovery. Be certain of what you're deleting before you press Enter. Never run rm -rf on a path you can't name exactly โ an unset variable or a typo can widen the target catastrophically. You'll see concrete examples in the flaw drill below.
rmdir โ remove an empty directory
$ rmdir empty_folder/
rmdir only works on empty directories โ it refuses if anything is inside. That's a safety feature. To delete a directory and its contents, use rm -r. Consider rmdir the "are you really sure this is empty?" check before the more powerful command.
Getting unstuck โ reading the manual
You will regularly encounter a command you've never seen or can't remember. The shell has authoritative built-in help for everything:
man โ the manual
$ man ls # opens the manual for ls (press q to quit)
$ man cp
$ man zshbuiltins # built-in zsh commands like cd, echo, type
Man pages are the authoritative reference for every command. They're terse and assume context โ but once you know how to read them, they're faster than any search result. Use /word to search within a man page (same controls as less), n/N to jump between matches, q to quit.
Reading strategy: skip the dense SYNOPSIS block at the top. Go to DESCRIPTION first for a plain-English explanation, then EXAMPLES if they exist. Return to the flag listing as a lookup reference, not as reading material.
--help
$ git --help
$ python3 --help
$ wrangler --help
Most modern tools respond to --help with a concise usage summary. Faster than man for a quick flag reminder; less complete for deep questions.
type and which โ what is this command?
$ type python3
python3 is /usr/local/bin/python3
$ which python3
/usr/local/bin/python3
$ type cd
cd is a shell builtin
type is more complete than which โ it tells you whether a command is a shell builtin (like cd), an alias, a shell function, or an external binary on disk. which only finds external binaries. When you're debugging "why is this running the wrong Python?" โ type python3 is your first move.
macOS note: on macOS, open is a Finder bridge. open . opens the current directory in Finder. open file.pdf opens it in Preview. It's the one command that crosses back from the shell to the GUI world.
A first taste of power โ redirection and pipes
The real leverage of the shell is combining commands. Here's a brief preview; T2 covers this in depth.
Output redirection: > and >>
$ echo "hello world" > greeting.txt # write to a file (creates or overwrites)
$ echo "second line" >> greeting.txt # append to the file
$ ls -l > file_list.txt # save command output to a file
When you run cmd > file.txt, the shell opens file.txt and empties it to zero bytes before running the command. If the command then fails or produces no output, the file is empty. This is one of the most common accidental data-loss patterns in shell work. Use >> when you're not certain you want to overwrite โ it appends instead.
Pipes: |
$ ls -l | less # page through ls output
$ cat app.log | tail -n 20 # last 20 lines of a log
The pipe character | takes the output of the command on the left and feeds it as input to the command on the right โ no temp file needed. This is how you build powerful one-liners from small, focused tools. You'll build on this heavily in T2.