Jul 16, 2026

macOS Command Line from Scratch

A command-line manual for tech beginners, starting with the terminal and progressing through Git and the Apple developer toolchain.

How to remember the content of this article: Almost no command is named randomly—they are abbreviations with origins, or metaphors with history (Chapter 0 covers the historical context behind them). Command names generally fall into three categories:

  1. Abbreviation-based: pwd = print working directory. Expand the abbreviation, and you will never forget it.
  2. Compound-based: mkdir = make directory. Portions of two words combined together.
  3. Metaphor-based: Git's stash (a hiding place), macOS's caffeinate (caffeine, preventing sleep). Understand the metaphor, and the command becomes visual.

The origin/etymology of each command is annotated throughout this guide. First ask "what is this an abbreviation for," then remember its usage—this is the easiest way to master the command line.

0. Understanding the Terminal: Where It All Begins

The GUI is so convenient, so why use the command line?

Because they excel at different tasks. Graphical User Interfaces (GUIs) are great for "viewing" and "exploring"; the command line excels at four things that GUIs struggle with:

  1. Precision. "Find all files in this folder that are larger than 100MB and haven't been touched in three months"—you can't easily click your way to this, but the command line does it in a single line.
  2. Composition. Commands can be pieced together like building blocks (using pipes, discussed in Chapter 3). Dozens of small tools can be combined to unlock endless capabilities.
  3. Automation. Commands can be saved as scripts to run repeatedly or on a schedule; you cannot "replay" a series of clicks in Finder from yesterday.
  4. Ubiquity. Servers do not have GUIs. When deploying websites or troubleshooting remotely, the command line is your only entry point.

For developers, there is another layer: core tools like Git, build tools, and package managers are entirely command-line programs at their core—GUI tools are merely wrappers around them. Learning the underlying commands keeps you from being limited by the wrapper.

A Bit of History: Your Mac is a Descendant of Unix

Let's rewind to 1969 at Bell Labs. Two engineers, Ken Thompson and Dennis Ritchie, wrote an operating system on an idle minicomputer and named it Unix. Most of the commands on your Mac today were born during those years.

Back then, there were no monitors. People interacted with computers using teletypes (abbreviated as TTY)—essentially a typewriter connected to the computer. You typed a line of text, it was sent to the host computer over a wire, and the host's response was printed directly onto paper. This typewriter sat at the end of the communication line, which is why it was called a terminal—the end of the line.

The term persists today. The Terminal.app you open is essentially a software emulation of that teletype: the line-by-line text and Q&A interaction style are inherited directly from the paper-tape era. (You can see its lineage everywhere in macOS: terminal devices are still called tty, and the ps command output has a TTY column.)

Let's look at the family tree. Unix later branched out. One branch was refined at UC Berkeley (BSD) and was chosen by Steve Jobs as the system foundation for NeXT, the company he founded after leaving Apple. In 1997, Apple acquired NeXT, and this system evolved into Mac OS X—which is today's macOS.

text
1969  Unix (Bell Labs)
 ├── 1978  BSD (Berkeley Software Distribution)
 │      └── 1989  NeXTSTEP (Steve Jobs' NeXT Computer)
 │             └── 2001  Mac OS X ——→ Today's macOS
 └── 1991  Linux (A free version rewritten from scratch by Linus Torvalds, referencing Unix)

Two conclusions that actually matter to you:

  • macOS is "certified" UNIX (officially Unix-compliant). Its command-line heritage spans over 50 years; it is not a feature tacked on later.
  • Linux is a "conceptually similar" rewrite of Unix, and the vast majority of commands are shared with macOS. What you learn here will work exactly the same way on any Linux server you rent in the future—learn it once, use it on both Mac and servers.

Why are command names so "cryptic"?

With history in mind, we can answer the most common point of confusion for beginners. Teletypes could only transmit about 10 characters per second, and each keystroke required waiting for a mechanical clack on paper—typing had a real physical cost. Consequently, the pioneers compressed commands to the absolute limit: "list" became ls, "change directory" became cd, and "copy" became cp.

This wasn't to be mysterious; it was the "data saving" of that era. A widely shared Q&A: years later, when someone asked Ken Thompson what he would change if he were to redesign Unix, he replied: "I'd spell create with an 'e'." (Referring to the creat system call)—even a single letter 'e' had to be saved back then.

Therefore, these short commands are compressed packages with a clear source. The approach of this guide is to help you "decompress" each package back to its original form—once you understand the expanded words, the abbreviations will naturally stick.

Terminal vs. Shell: They are two different things

  • Terminal (Terminal.app): The black (or white) window. It is only responsible for displaying text and receiving keyboard input. It does not understand any commands itself—just as the teletype of yesteryear did not understand the characters you typed.
  • Shell: The interpreter program running behind the window that does the actual work. You enter commands, it parses and executes them, and sends the results back to the terminal for display. The default Shell in macOS is zsh.
Etymology
"Shell" literally means an outer casing—the core of the operating system is called the "kernel". The Shell wraps around the kernel as the intermediary layer for human-computer interaction. The shell and the kernel: a metaphor that runs throughout the operating system. zsh = Z Shell, named after Zhong Shao, a teaching assistant at Princeton when the author of zsh was studying there. Its predecessor, bash = Bourne-Again SHell, is a double entendre: it is a "reincarnation of the Bourne Shell" (honoring its original author Stephen Bourne) and sounds like "born again".

How to open it: Press Cmd + Space for Spotlight, type "Terminal", and press Enter. You will see a prompt:

text
jerry@MacBook-Pro ~ %
│     │           │ └── %: The zsh prompt, meaning "it's your turn to speak" (bash uses $, root uses #)
│     │           └──── Current location (~ represents your home directory, detailed in the next chapter)
│     └──────────────── Machine name
└────────────────────── Username

In the examples that follow, the only part you need to type is what comes after the prompt.

Anatomy of a Command

bash
ls -l -a ~/Desktop
│  │  │  └── Argument: What to operate on (here, the desktop directory)
│  │  └───── Option: Modifies behavior
│  └──────── Option: Modifies behavior
└─────────── Command: What to do

The structure rules are simple, and almost all commands follow them:

bash
ls -l -a             # Short options: single dash + single letter
ls -la               # Short options can be combined
git log --oneline    # Long options: double dash + full word, common in modern tools (git, brew, swift)

One macOS quirk to know in advance: old-school Unix commands built into the system (like ls, cp, rm derived from BSD) mostly only recognize short options—running ls --all will throw an error. Long options are characteristic of GNU and modern tools.

Option letters also follow common conventions. Note: they are conventions, not strict standards—the same letter may mean different things in different commands. When in doubt, trust the man page. However, it's still worth memorizing these high-frequency conventions:

OptionCommon Full NameMeaning
-aallShow all files (including hidden ones)
-llongLong format (detailed information)
-hhuman-readableHuman-readable (displays 1048576 as 1M); when used alone, often stands for help
-RrecursiveRecursive (includes subdirectories). Note: lowercase -r is also recursive in cp and rm, but in ls it stands for reverse—a classic example of "conventions have exceptions."
-fforceForce action, do not prompt for confirmation
-vverboseVerbose mode (outputs detailed progress); sometimes stands for version
-iinteractiveInteractive mode (asks before proceeding)
-nnumberCount / quantity

Troubleshooting: Three ways to help yourself when stuck

bash
man ls        # manual: Official comprehensive documentation. Press q to exit, / to search.
ls --help     # Supported by most commands; outputs a brief usage summary.
tldr ls       # Third-party tool (see Chapter 10): gives only the most common examples; a beginner's favorite.
Etymology
man = manual. tldr = too long; didn't read—the name says it all: man pages are too long; just give me examples.

Keyboard Survival Skills

text
Tab          Autocomplete commands and paths (the single most important key)
↑ / ↓        Cycle through command history
Ctrl + C     Force terminate the current command
Ctrl + A / E Move cursor to the beginning / end of the line
Cmd + K      Clear the screen (or type the `clear` command)

Safety Rules for Beginners

  1. Deleting via the command line bypasses the Trash. Once deleted with rm, it is gone.
  2. Be highly alert when you see `sudo`—it runs commands with administrative privileges, and the system will not stop you from breaking things.
  3. Do not copy and paste commands you do not understand, especially those containing sudo, rm -rf, or curl ... | sh.
  4. When in doubt, check with man or tldr before pressing Enter.

1. Navigation: Where Are You, What's There, and Where to Go

First, clarify three fundamental concepts: files, directories, and file systems

  • File: A named chunk of data. A piece of code, an image, or an app are all files to the operating system.
  • Directory: A container for files (and other directories)—this is exactly what the GUI calls a "folder"; two names for the same thing.
Etymology
"Directory" originally means a list or index (like a phone directory). This name reveals its essence: a directory is actually a list that records the mapping of "name → location on disk", much like a phone directory maps "name → phone number". The folder metaphor (folder) is an office analogy introduced later by GUIs.
  • File System: All directories nest within one another, forming an upside-down tree. The root of the tree is /, known as the root directory. Every file on your computer can be reached by starting from this root:
text
/                      ← Root: Everything grows from here
├── Applications       ← Where all apps live
├── System             ← macOS system files (leave alone)
└── Users              ← All user profiles
    └── jerry          ← Your home directory, abbreviated as ~, where your files live
        ├── Desktop
        ├── Documents
        └── Projects
            └── MyApp

Double-clicking a folder in Finder and running cd in the command line traverse the same tree—one uses a mouse, the other a keyboard.

One final concept: the command line doesn't have a "current window," but it has an equivalent: the working directory, which is the node on the tree where you are currently "standing". All relative paths are evaluated from this baseline.

Three Survival Commands

Beginners should memorize these three commands, which answer three core questions:

pwdWhere am I?

Etymology
print working directory.
bash
pwd
# Output: /Users/jerry/Projects

lsWhat is here?

Etymology
list. In early Unix days, commands were compressed to two letters to minimize typing.
bash
ls              # List current directory
ls -l           # long: Detailed mode (permissions, size, modification time)
ls -a           # all: Include hidden files
ls -la          # Combined: detailed + all
ls -lh          # Detailed + human-readable file sizes
ls ~/Desktop    # View another directory without navigating to it

About hidden files: Files whose names start with a dot . are hidden by default (e.g., .gitignore, .zshrc). This is a Unix convention: configuration files are hidden so they don't clutter your daily view. They aren't mysterious—you can see them in Finder by pressing Cmd + Shift + ..

The output of ls -l has high information density. Learning to read it is like getting a Finder properties dialog in a single line:

text
-rw-r--r--  1 jerry staff  1024  7 14 10:30 README.md
│           │ │     │      │     │          └── File name
│           │ │     │      │     └── Last modification time
│           │ │     │      └── Size (in bytes; -h displays as 1K)
│           │ │     └── Group owner
│           │ └── File owner
│           └── Link count (can be ignored by beginners)
└── Type and permissions: First character '-' is a file, 'd' is a directory;
    the following 'rwx' = read/write/execute permissions (detailed in Chapter 4 chmod)

cdGo somewhere else

Etymology
change directory.
bash
cd Projects         # Enter the Projects folder under the current directory
cd /Users/jerry     # Navigate directly using an absolute path
cd ..               # Go up one level
cd ~                # Go home (user's home directory)
cd                  # Go home (shortcut if no arguments are provided)
cd -                # Toggle back to the previous directory (very handy for bouncing back and forth)

Path Notation System

NotationMeaningMnemonic
/Root directory (the starting point of the entire file system)root
~Your home directory (/Users/your_username)home, the tilde looks like a roof
.Current directoryOne dot = right here
..Parent directoryTwo dots = one level up
-Previous directoryMinus sign = go back

Paths starting with / are absolute paths (resolved from the root, valid from anywhere); paths that do not start with / are relative paths (resolved from your current working directory).

Practice: cd ~ to go home → ls -la to check what's there → cd Desktoppwd to confirm location → cd - to jump back.


2. File Operations: Create, Copy, Move, and Delete

mkdirCreate Directory

Etymology
make directory.
bash
mkdir Notes                  # Create a single directory
mkdir -p App/Sources/Views   # -p = parents: Create parent directories if they don't exist

touchCreate an Empty File

Etymology
Literally "touch". Its original purpose was to update a file's modification timestamp (touching a file makes it look "new"); if the file doesn't exist, it creates an empty one as a side effect—which has ironically become its most common use.
bash
touch README.md

cpCopy

Etymology
copy.
bash
cp a.txt b.txt               # Copy a file and name the duplicate
cp a.txt ~/Desktop/          # Copy a file to another location
cp -R Sources/ backup/       # -R = recursive: Copying directories requires recursion

mvMove (also handles Renaming)

Etymology
move. To the file system, "renaming" is just "moving a file to a new name in the same directory", so one command handles both.
bash
mv draft.md final.md         # Rename
mv final.md ~/Documents/     # Move

rmDelete (Danger Zone)

Etymology
remove.
bash
rm a.txt                     # Delete file
rm -r build/                 # recursive: Delete directory
rm -rf build/                # + force: Delete immediately without asking questions. Double-check path before hitting Enter.
rmdir empty_dir              # remove directory: Can only delete empty directories (hence rarely used)

Once again: `rm` does not go to the Trash. Appending a wrong path to rm -rf is the classic command-line horror story.

Etymology
link; -s = symbolic, i.e., a "soft" link, similar to Finder's alias.
bash
ln -s /path/to/real/folder ~/shortcut

tar / zipArchiving and Compression

Etymology
tar = tape archive. The name shows its age: its original job was to pack files together and write them onto magnetic tape for backups. The tapes are long gone, but the command lives on. zip refers to a zipper: pulling things together into a single package.
bash
tar -czf backup.tar.gz Sources/    # Compress and archive (c=create, z=gzip compress, f=file specifies filename)
tar -xzf backup.tar.gz             # Extract archive (x=extract)
tar -tzf backup.tar.gz             # List contents without extracting (t=list table)

zip -r archive.zip folder/         # Zip format, more universal for cross-platform sharing
unzip archive.zip -d output/       # -d = destination: Extract to a specific directory

The option combinations for tar look like spells, but you only need to remember two: czf compresses, xzf extracts—just the difference between create and extract.

openReturn to the GUI World (macOS Specific)

Etymology
Literally "open". It acts as a portal between the terminal and Finder/Apps. Linux users do not get this luxury.
bash
open .                        # Open current directory in Finder (used daily)
open MyApp.xcodeproj          # Open with default app (Xcode)
open -a Safari https://apple.com   # -a = application: Specify which app to use
open -R file.swift            # -R = reveal: Locate the file in Finder

3. Viewing and Searching: Reading Files, Finding Content, and Chaining Commands

catPrint Entire File to Screen

Etymology
concatenate. Its primary job is to concatenate multiple files end-to-end and output them. cat single_file is merely a byproduct of this capability, yet it became the default way to view files.
bash
cat notes.txt
cat a.txt b.txt > merged.txt   # Core duty: Concatenate two files

lessPaginate Large Files

Etymology
A programmer's inside joke. Early pagination tools were named more ("give me another page"). The improved version was named less as a play on the proverb "less is more."
bash
less app.log
# Controls: Space to scroll down, /keyword to search, n for next match, q to exit

head / tailView Start / End of a File

Etymology
Literally the "head" and "tail".
bash
head -n 20 file.log     # View first 20 lines
tail -n 50 file.log     # View last 50 lines
tail -f app.log         # -f = follow: Stay attached to the file and print updates in real-time (essential for log-watching)

wcCounting Items

Etymology
word count. However, its most common use is counting lines.
bash
wc -l file.swift        # -l = lines: Count number of lines

grepSearching Inside Content

Etymology
The most interesting etymology in this guide. In the ancient editor ed, the instruction to "search globally (g) for a regular expression (re) and print (p) matching lines" was written as g/re/p. Because people used it so much, it was split off into its own command. Thus, grep is literally "global regular expression print."

Now is a good time to introduce regular expressions (regex): a mini-language using symbols to describe "what kind of text counts as a match"—^ for start of line, $ for end of line, . for any single character, and * to repeat the preceding match. Beginners can treat grep as a standard text search at first. When you eventually need to "match all lines starting with 'test'," you can learn regex; it is well worth it.

bash
grep "TODO" main.swift          # Search within a file
grep -rn "TODO" Sources/        # -r recursive search, -n show line numbers (most common combo)
grep -i "error" app.log         # -i = ignore case
grep -v "debug" app.log         # -v = invert: Exclude lines containing "debug"
grep -c "func" main.swift       # -c = count: Number of matching lines (multiple occurrences in a single line only count as one line)

findFind Files by Name/Attributes

While grep searches "inside content", find searches the "files themselves" (by name, size, modification time, etc.).

bash
find . -name "*.swift"                 # Find Swift files recursively from current directory
find . -name "*.png" -size +1M         # Find PNG images larger than 1MB
find . -name ".DS_Store" -delete       # Find and delete (cleanup Finder cache files)

Pipes and Redirections: The Soul of Unix

Unix Philosophy: Write programs that do one thing and do it well. Write programs to work together. This quote comes from Doug McIlroy, the inventor of pipes. His original phrasing is worth reading:

Write programs that do one thing and do it well. Write programs to work together. Write programs to handle text streams, because that is a universal interface.

The commands we've learned are indeed "small": ls only lists, grep only searches, and wc only counts. But once thirty small commands can be chained together freely, the operations they can express are infinite—this is why the command line remains irreplaceable after sixty years. The syntax for chaining relies on just three symbols:

bash
CommandA | CommandB     # Pipe: Send output of A as input to B
Command > file          # Redirection: Write output to file (overwrite)
Command >> file         # Append output to file
Etymology
| resembles a pipe. Data flows like water from left to right.

Let's experience the power of composition:

bash
ls -la | grep "\.swift"                # List directory, show only Swift files (\. escapes the regex dot back to a literal period)
grep -r "TODO" Sources/ | wc -l        # How many lines contain TODO in the project?
history | grep "git"                   # What git commands did I run earlier?
xcodebuild ... > build.log 2>&1        # Archive build logs (2>&1 redirects standard error to stdout)

Handy Utilities

bash
echo "Hello"            # Echo: Print text as-is
which swift             # "Which one": Outputs the path to the command's executable file
history                 # List of previously run commands
Etymology
echo returns whatever you call out. which answers "which file does the system execute when I type 'swift'?"

which brings up an important concept you should understand now: PATH.

Have you ever wondered how the Shell knows where to find the ls program when you run it? The answer is an environment variable (a global shell setting) named PATH. Its value is a list of directories separated by :. The Shell searches these directories in order for an executable with that name and runs the first one it finds.

bash
echo $PATH        # Print your path list ($ prefix means "value of this variable")
# /opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:...

This explains two common beginner experiences: the error command not found means "searched all directories in the path list, but no executable matches this name" (usually because it's not installed, or installed outside the PATH); and when Homebrew asks you to modify ~/.zshrc after installing a tool, it's usually adding its installation directory to your PATH.


4. System Administration: Processes, Disks, and Permissions

Let's establish a basic concept: a program is a set of files sitting on disk; a process is an active instance of that program running in memory—a live copy. Opening two Safari windows might run the same program and possibly the same process; however, running ls creates a short-lived process that terminates after finishing its work. Every process gets a number when it is born: its PID (process identifier).

You are already familiar with process managers—macOS's Activity Monitor is one. ps and top are its command-line counterparts.

ps / topInspecting Processes

Etymology
ps = process status. top refers to the top of the list—processes sorted by resource usage, so the most CPU-heavy ones sit at the top.
bash
ps aux                      # List all processes (a=include other users, u=detailed format, x=include background processes without controlling terminals; combined ≈ all processes)
ps aux | grep Simulator     # Find a specific process using a pipe
top -o cpu                  # Real-time monitor sorted by CPU usage; press q to exit

killTerminating Processes

Etymology
Despite the aggressive name, its core mechanism is gentle: it sends a "signal" to a process. The default signal happens to be "please exit." -9 is signal number 9, SIGKILL: no negotiations, terminate immediately.
bash
kill 12345          # Politely request process with PID 12345 to exit
kill -9 12345       # Force terminate (when the process is unresponsive)
killall Simulator   # Terminate by name (kill all matching)

sudoRun with Administrative Privileges

Etymology
superuser do. The superuser is the unrestricted administrative account in Unix, named root. It owns the root directory / and sits at the very base of the permission tree.
bash
sudo xcode-select -s /Applications/Xcode.app
# Prompts for your login password; remembers authentication for a short grace period

Keep Safety Rule #2 in mind: the system does not stop root. You are responsible for what you run.

chmod / chownPermissions and Ownership

Etymology
change mode (change permission mode), change owner. Part of the ch- prefix family.
bash
chmod +x deploy.sh     # Add "executable" permission to the script (if a script won't run, this is likely why)
ls -l                  # The leading 'rwxr-xr-x' indicates permissions: r=read, w=write, x=execute

df / duDisk Space Remaining / Used

Etymology
disk free and disk usage. Mirror commands: df shows "how much is left," while du shows "how much is used."
bash
df -h                          # Remaining space on each partition (-h for human-readable)
du -sh *                       # Size of each item in the current directory (-s = summary, total size only)
du -sh * | sort -rh | head -5  # Combo: Find the 5 largest items
du -sh ~/Library/Developer/*   # Where developer disk space usually goes to die

Naming Families: -ctl, -util, and -d

System commands in macOS/Unix often share three suffixes. Recognizing them helps you guess what an unfamiliar command does:

SuffixMeaningExample
-ctlcontrol: Controller for a servicelaunchctl (controls launchd), simctl (controls simulator)
-utilutility: A toolbox for a specific domaindiskutil (disks), plutil (plists), textutil (documents)
-ddaemon: A background servicelaunchd, sshd — background processes, not meant to be run directly by users
"Daemon" (guardian spirit) is a classical, romantic Unix term for background services. launchctl is pronounced "launch control": it manages macOS's primary service manager, launchd.
bash
diskutil list                          # List disks and partitions
launchctl list | grep com.apple       # Currently loaded background services
uname -m                               # unix name: Check architecture (arm64 / x86_64)
sw_vers                                # software version: macOS version details

5. macOS-Specific Commands: Apple's Naming Taste

These commands are unique to macOS and not available on Linux. Apple's naming style tends to be more descriptive and complete than traditional Unix commands, making them easier to remember.

pbcopy / pbpasteClipboard Pipeline

Etymology
pasteboard. This is Apple's official term for the clipboard. (If you write Apple apps, the corresponding APIs are indeed NSPasteboard / UIPasteboard—the command line and framework share the same lineage).
bash
cat ~/.ssh/id_ed25519.pub | pbcopy    # Copy public key to clipboard to paste on a website
pbpaste > snippet.swift               # Save clipboard contents to a file
pbpaste | wc -l                       # How many lines are in the clipboard?

mdfindSpotlight in the Command Line

Etymology
metadata find. Spotlight works on top of a metadata index: find traverses the file system in real-time, while mdfind queries the pre-built index, which is why it is usually much faster. The trade-off is its dependency on the index—it won't find files in directories excluded from Spotlight, or newly written files that haven't been indexed yet. In those cases, you'll still need to use find.
bash
mdfind -name "Package.swift"                  # Search the entire drive by name (indexed search, usually instant)
mdfind -onlyin ~/Projects "URLSession"        # Limit scope; can also search inside file contents for indexed file types

defaultsRead/Write User Preferences

Etymology
Named after macOS's "user defaults database". The UserDefaults API you use inside apps interacts with this exact system—this command is its direct entry point.
bash
defaults read com.yourname.MyApp        # View your app's stored defaults (great for debugging UserDefaults)
defaults delete com.yourname.MyApp      # Clear them to simulate a fresh install
defaults write com.apple.finder AppleShowAllFiles -bool true && killall Finder
                                        # Force Finder to show hidden files

caffeinatePrevent Sleep

Etymology
The best command name in this chapter. caffeinate = "give caffeine to...". Give your Mac coffee, and it won't sleep.
bash
caffeinate -i ./long_task.sh    # Prevent system sleep while the task runs; auto-releases on finish
caffeinate -d -t 3600           # display: Keep display awake for 1 hour (3600 seconds)

sipsCommand-Line Image Processing

Etymology
scriptable image processing system.
bash
sips -z 512 512 icon.png --out icon-512.png     # Resize (height, then width after -z)
sips -s format jpeg in.png --out out.jpg        # Convert format
sips -g pixelWidth -g pixelHeight image.png     # -g = get: Query properties

You can wrap this in a loop to generate all App Icon sizes without opening any graphic design software.

Other Fun and Useful Commands

bash
say "Build complete"         # Text-to-speech. Append to long commands: xcodebuild ... && say done
screencapture -i shot.png    # Take a screenshot (-i = interactive selection)
ditto -c -k --keepParent MyApp.app MyApp.zip
                             # Copy/archive while preserving all macOS metadata; use for packaging before notarization
networkQuality               # Apple's built-in internet speed test (macOS 12+)
qlmanage -p file.pdf         # Trigger Quick Look from command line (ql = Quick Look)
Etymology
ditto is an English word derived from Latin, meaning "same as above" or "copy exactly"—a more faithful duplicate than cp. say does exactly what it says.

6. Homebrew: The "Brewery" of Mac Software

What is a package manager: The App Store of the command-line world—responsible for installing, updating, and uninstalling software. Crucially, it does one key thing the App Store doesn't: resolve dependencies. Command-line tools often rely on other tools (A depends on B, B depends on C). A package manager traces this dependency tree and installs all required components in a single command. This is a universal concept; you'll encounter its siblings like Linux's apt, JavaScript's npm, and Swift's SPM—the core ideas are identical.

macOS does not come with a built-in package manager. Homebrew is the de facto community standard. All tools in Chapter 10 are installed using it.

Etymology
Homebrew = "beer brewed at home". The author compared compiling software on a Mac to home brewing and built an entire nomenclature around this metaphor. Understand the metaphor, and all concepts click:
TermLiteral MeaningActual Meaning
formulaRecipeInstallation script for a command-line tool
caskBarrelGraphical applications (large packages)
bottleBottled beerPre-compiled packages (no compiling needed, ready to drink)
tapBeer tapAccessing third-party software sources (turn on the tap, beer flows)
cellarWine cellarThe actual installation directory

Install Homebrew first (using the one-liner on brew.sh), and then:

bash
brew install ripgrep                  # Install a command-line tool (formula)
brew install --cask rectangle        # Install a GUI app (cask)
brew search json                      # Search for packages to install
brew info node                        # View package details
brew list                             # List installed packages
brew upgrade                          # Upgrade all installed packages
brew uninstall node                   # Uninstall a package
brew cleanup                          # Remove old versions to free disk space
brew doctor                           # System check for environmental issues

Two advanced tips:

bash
# Environment Migration: Export a list of all current brew packages and reinstall them on a new Mac
brew bundle dump          # Generates a Brewfile
brew bundle               # Installs everything listed in the Brewfile

# Background Service Management (local databases, etc.)
brew services start postgresql
brew services list

7. Git: Snapshotting Your Code

What Problem Does it Solve?

You have likely seen a folder resembling: report-final.docx, report-final2.docx, report-really-final.docx. Version control is the structured solution to this dilemma: it records every meaningful change in your project as a snapshot (known as a "commit" in Git). You can inspect any snapshot, compare differences, roll back to any state, or merge changes from multiple people without conflicts. Developers need this to revert mistakes and collaborate seamlessly.

Git was born in 2005. Linus Torvalds needed a tool to manage the globally distributed Linux kernel codebase. Frustrated with existing options, he spent two weeks writing the initial prototype. Today, it is the absolute industry standard.

Origin of the Name

Etymology
"Git" is British slang for an unpleasant or contemptible person. Linus Torvalds jokingly remarked: "I'm an egotistical bastard, and I name all my projects after myself. First 'Linux', now 'git'." The official manual self-deprecatingly describes it as "the stupid content tracker."

Build a Mental Model First, Then Memorize Commands

All Git commands move assets among these four zones. Understand this diagram, and every command finds its context:

text
Working Directory      Staging Area          Local Repository       Remote Repository
  (working dir)       (staging area)           (repository)              (origin)
Files you edit    →   Staged changes     →   Saved snapshot history  →   Copy on GitHub
               git add             git commit               git push
               ←─────────────────── git restore             ←─── git pull / fetch
  • Staging Area: A list of changes prepared for the next commit. If you edited 10 files, you can stage only 3 to build a clean, focused commit.
  • commit: A snapshot. The word is a double entendre—both the action of submitting and a commitment to record it in history.
  • origin: The default name for the remote repository you cloned from.

7.1 Initial Configuration (One-Time Setup)

bash
git config --global user.name "Jerry"
git config --global user.email "[email protected]"
git config --global init.defaultBranch main
config --global writes settings to ~/.gitconfig, shared across all repositories on your system.

7.2 The Daily Loop: Get Through the Day with Five Commands

Etymology
clone (copy a remote repository); status (check current state); add (stage changes); push/pull (transfer data relative to your computer: push out, pull in).
bash
git clone https://github.com/user/repo.git    # First time: Clone a repository
git status -sb            # Check current status (-s = short, -b = show branch)
git add -p                # Interactive staging: review changes patch-by-patch (better for clean commits than add .)
git commit -m "feat: support dark mode"
git push                  # Push commits to remote
git pull                  # Pull updates from remote
fetch vs pull: fetch only downloads new remote commits without touching your local files; pull = fetch + integration—integrated using merge by default, but can also be configured to rebase (git config pull.rebase true). Use fetch to inspect remote changes before merging.

7.3 Inspecting History: What Happened?

Etymology
log (records of history, like a ship's logbook); diff = difference; show (display details); blame (literally assign responsibility for a line of code: "who wrote this?!").
bash
git log --oneline --graph -10     # Last 10 commits: one line per commit + branch topology graph
git log -p file.swift             # Detailed history of changes to a file (-p = patch)
git diff                          # What changed (Working Directory vs. Staging Area)
git diff --staged                 # What is ready to commit (Staging Area vs. Last Commit)
git show abc123                   # View changes introduced in a specific commit
git blame -L 10,20 file.swift     # Lines 10–20: who modified them and in which commit

Good practice to adopt: Run git diff --staged before every commit—verify that what you are committing is exactly what you intend. Many errors are caught here.

7.4 Branches: Parallel Dimensions

Etymology
"Branch" is a branch on a tree—growing its own commits off the main trunk before being merged back. HEAD is your current location: the commit you currently have checked out.
bash
git switch -c feature/dark-mode    # -c = create: Create and switch to a branch (switch is the modern, clear command)
git switch main                    # Switch back to the main branch
git branch                         # List branches, * indicates the current branch
git merge feature/dark-mode        # Merge the feature branch into the current branch
git branch -d feature/dark-mode    # Delete a merged branch (-d = delete)

7.5 Recovery: The Precise Meaning of Each "Undo" Command

Git has multiple undo commands because "regret" takes many forms. Use the etymology to guide you:

Etymology
restore (return to original state); reset (rewind the history pointer); revert (apply an offsetting commit without changing history); amend (modify the last commit); stash (hide away unfinished changes); reflog = reference log (the black box recording every move of HEAD).
bash
git restore file.swift             # Regret edits: Discard changes in Working Directory
git restore --staged file.swift    # Regret add: Remove file from Staging Area
git commit --amend                 # Regret last commit: Amend commit message or add missing files
git reset --soft HEAD~1            # Undo last commit, keep changes staged (--soft)
git reset --hard HEAD~1            # Undo last commit and discard all changes (--hard; use with caution)
git revert abc123                  # Undo a pushed commit: creates a new offsetting commit without rewriting history
git stash                          # Need to switch branches mid-task: stash current progress
git stash pop                      # Retrieve stashed changes on return (pop off the stack)
git reflog                         # Black box: Retrieve coordinates if you reset incorrectly or deleted a branch
git reset --hard HEAD@{2}          # Rewind to the state before a bad operation using reflog coordinates

Remember reflog—most committed items can be recovered here. However, do not treat it as a backup: it only exists locally on your machine, and entries are periodically cleaned up (by default, reachable entries expire in about 90 days, and unreachable ones in about 30 days). The only true insurance remains pushing to a remote repository in a timely manner.

7.6 Advanced Quartet

Etymology
rebase = re-base: Transplant your branch to a new starting point, resulting in a linear history; cherry-pick: Pluck a specific commit from another branch; bisect = bi(two) + sect(cut): Perform a binary search; worktree: Grow multiple working trees/directories from the same repository.
bash
git rebase main               # Re-base current branch onto the tip of main
git rebase -i HEAD~3          # -i = interactive: Squash, edit, or discard the last 3 commits (clean history before pushing)

git cherry-pick abc123        # Apply a specific commit to the current branch

git bisect start              # Perform binary search to locate the commit that introduced a bug:
git bisect bad                #   Mark the current version as problematic
git bisect good v1.2.0        #   Mark a known good version (e.g., v1.2.0)
                              #   Git automatically switches commits; test and mark until it points to the culprit
git bisect reset              # Finish and return to your original branch

git worktree add -b hotfix/crash ../hotfix   # -b creates a new branch and checks it out to a new directory; work on a hotfix concurrently without stashing (omit -b and write the branch name if it already exists)

7.7 .gitignore

Tell Git which files to never track. Common templates for Apple + Web projects:

gitignore
.DS_Store
xcuserdata/
DerivedData/
.build/
Pods/
node_modules/
.env

8. Networking Commands

Let's lay down three fundamental networking concepts:

  • IP Address: The address of a device on the network, e.g., 17.253.144.10.
  • Domain and DNS: Numbers are hard to remember, hence domain names like apple.com. The DNS (Domain Name System) is the global directory translating domain names to IP addresses.
  • Port: The doorway number for specific services on an IP address. Web traffic goes through port 80 (HTTP) and 443 (HTTPS); local development servers often use 3000 or 8080. "Port is in use" means another process is already guarding that doorway.

pingIs the other side alive?

Etymology
Onomatopoeia for submarine sonar: emitting a pulse and listening for the echo to calculate distance. Ping operates identically: send a small packet, wait for the echo, and report round-trip time.
bash
ping -c 4 apple.com     # -c = count: Send exactly 4 packets (without it, ping runs indefinitely; stop with Ctrl+C)
# Output shows resolved IP and round-trip milliseconds. If it pings successfully, the network is reachable;
# if it doesn't, don't rush to conclusions—many servers and firewalls block ping packets entirely.

curlCommand-Line Browser

Etymology
client for URLs. It sends requests for any URL, making it the primary tool for testing APIs.
bash
curl https://api.github.com/zen          # Simple GET request, print response
curl -s https://api.example.com/users | jq    # -s = silent: Hide progress; pipe to jq to format JSON
curl -LO https://example.com/file.zip    # Download: -L follows redirects, -O saves with original filename
curl -sI https://example.com             # -I = head: Print headers only
curl -v https://api.example.com          # -v = verbose: Print full request/response transaction for debugging

# Send a POST request (verify API behavior before writing app code)
curl -X POST "https://api.example.com/users" \
  -H "Content-Type: application/json" \
  -d '{"name": "Jerry"}'
Options follow naming conventions: -X specifies method, -H = Header, and -d = data.

ssh / scpSecure Remote Login and File Transfer

Etymology
secure shell (encrypted remote shell) and secure copy (encrypted version of cp; argument order matches cp: source, then destination).
bash
ssh [email protected]                          # Log in to a remote server
ssh-keygen -t ed25519 -C "[email protected]"   # key generator: Generate a key pair
ssh-copy-id [email protected]                  # Copy public key to server for passwordless login
scp file.zip [email protected]:/var/www/       # Upload file
scp [email protected]:/var/log/app.log .       # Download file to current directory (.)

Key Pair Metaphor: ssh-keygen generates a lock (public key, shareable) and a key (private key, kept secure on your Mac). ssh-copy-id mounts the lock on the server's door; after that, you can unlock the door using your private key. GitHub passwordless pushing uses the same mechanism: adding your public key to GitHub settings is "mounting the lock."

lsofWho is using this port?

Etymology
list open files. Why does a port lookup command list files? Because of the Unix philosophy: everything is a file—network connections are treated as files by the system.
bash
lsof -i :3000               # Which process is using port 3000? (Web development daily chore)
kill -9 $(lsof -ti :3000)   # Combo: Force-terminate that process (-t returns only PIDs, perfect for feeding into kill)

digQuery DNS Records

Etymology
domain information groper. Also matches the verb "dig"—digging up DNS records.
bash
dig apple.com +short         # Resolve domain name to IP (+short gives only the answer)
dig @8.8.8.8 apple.com       # Query using Google's DNS server (useful for isolating resolution issues)

If DNS modifications do not seem to take effect immediately, flush the cache:

bash
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder

9. Apple Developer Toolchain

Let's clarify the key verb: build. The entire process of turning human-readable source code into an executable app. This includes compiling (translating code into machine instructions), linking (combining modules and libraries), and packaging resources (icons, assets). Pressing ⌘B in Xcode performs a build; this chapter presents the command-line equivalent. Why do we need a command-line equivalent? Automation. A CI server (a machine that automatically builds and runs tests on code commits) has no one to press ⌘B.

Apple's modern tools avoid cryptic abbreviations, so names are self-explanatory.

xcode-selectSelect Xcode Version

Etymology
Literally "Xcode selector". Decides which Xcode installation commands default to when you have stable and beta versions installed.
bash
xcode-select --install       # Install Xcode Command Line Tools (required for git, etc. on a new Mac)
xcode-select -p              # -p = print path: Show current active Xcode path
sudo xcode-select -s /Applications/Xcode-beta.app    # -s = switch: Change active Xcode path

xcrunToolchain Portal

Etymology
Xcode run. The Xcode toolchain contains hundreds of utilities (simctl, notarytool, etc.) that are not in the system PATH. They are executed through xcrun. Read xcrun xxx as "run xxx from Xcode."
bash
xcrun --find swift               # Locate the tool's actual path
xcrun --show-sdk-path            # Find the active SDK path

xcodebuildCommand-Line Build Tool

Etymology
Xcode + build. The command-line equivalent of pressing ⌘B in Xcode.
bash
xcodebuild -list                                  # List schemes in the project
xcodebuild -scheme MyApp build                    # Build project
xcodebuild test -scheme MyApp \
  -destination 'platform=iOS Simulator,name=iPhone 16'    # Run tests on simulator
xcodebuild archive -scheme MyApp -archivePath ./build/MyApp.xcarchive   # Archive/Package project

The output can be verbose; use xcbeautify (Chapter 10) to format it.

simctlSimulator Controller

Etymology
simulator control—a member of the -ctl suffix family (Chapter 4).
bash
xcrun simctl list devices | grep Booted        # Find running simulators
xcrun simctl boot "iPhone 16 Pro"              # Boot simulator
xcrun simctl openurl booted "myapp://detail?id=42"   # Test deep linking (booted = current running simulator)
xcrun simctl push booted com.me.MyApp push.apns      # Send push notification to simulator
xcrun simctl io booted screenshot shot.png     # Capture screenshot
xcrun simctl status_bar booted override --time "9:41" --batteryLevel 100
                                               # Prepare app store screenshots: Full battery + 9:41 (honoring the original iPhone presentation time, a tradition in Apple marketing materials)
xcrun simctl delete unavailable                # Delete outdated simulators (often recovers dozens of gigabytes)
xcrun simctl erase all                         # Reset all simulators to factory settings

codesign / securitySignature Troubleshooting

Etymology
code + sign. security manages keychains, where certificates reside.
bash
codesign -dv --verbose=4 MyApp.app             # Check code-signing certificate used on the app
security find-identity -v -p codesigning       # List signing identities available on your machine

If you encounter "no certificate found" errors, use the second command to check if the certificate exists in your keychain.

notarytool + staplerNotarization Pipeline

Etymology
notarize. macOS apps signed with Developer ID certificates and distributed outside the App Store must first be submitted to Apple's notarization service for automated security scanning. Otherwise, they will be blocked by Gatekeeper when opened by users. stapler attaches ("staples") the verification ticket to the app, allowing users to verify its authenticity offline (while Gatekeeper can check online, stapling enables offline validation). A logical sequence of two descriptive tools.
bash
xcrun notarytool submit MyApp.zip \
  --apple-id [email protected] --team-id TEAMID \
  --password "app-specific-password" --wait               # Submit and wait for results
xcrun stapler staple MyApp.app                  # Attach verification ticket upon approval

plutilPlist Utility

Etymology
property list utility.
bash
plutil -p Info.plist                            # Print plist in a friendly format
plutil -lint Info.plist                         # Lint: check syntax
plutil -replace CFBundleShortVersionString -string "2.0" Info.plist   # Update version number programmatically

Crash Analysis Duo

Etymology
atos = address to symbol: maps hex addresses in crash logs back to function names. dwarfdump extracts DWARF debugging information. DWARF (dwarf) and ELF (elf, Executable and Linkable Format in Linux) are companion fantasy puns—a touch of programmer whimsy. Today, DWARF is a cross-platform debugging format standard, and macOS's Mach-O format uses it as well.
bash
dwarfdump --uuid MyApp.app.dSYM     # Verify if the dSYM matches the crash log UUID
atos -o MyApp.app.dSYM/Contents/Resources/DWARF/MyApp \
  -arch arm64 -l 0x100000000 0x1000a4c2c        # Resolve address to function name and line number

swiftSwift Toolchain and Package Management

bash
swift repl                     # Interactive REPL to test code snippets
swift run                      # Run an SPM executable project
swift build && swift test      # Build and run tests
swift package init             # Initialize a new Swift Package
swift package update           # Update dependencies

Regular Maintenance

bash
rm -rf ~/Library/Developer/Xcode/DerivedData   # The silver bullet for Xcode issues (removes build cache safely)

10. Modern Productivity Tools: A Compendium of Naming Easter Eggs

Install all of these with brew install <name>. They are modern replacements for traditional commands, offering faster execution, smarter defaults, and memorable names.

ripgrep (rg)Faster grep

Etymology
"rip" means to tear or move quickly—ripping through files to search. The abbreviation rg pays homage to the Unix command tradition.
bash
rg "TODO"                     # Search recursively, automatically respecting .gitignore (ignores node_modules, etc.)
rg -t swift "URLSession"      # -t = type: Limit search to a specific file type
rg -l "deprecated"            # Output only filenames of matching files

fdFaster find

Etymology
Truncating "find" gives "fd"—a direct representation of its product philosophy: shorter and simpler.
bash
fd '\.swift$'                 # Find Swift files (cf. find . -name "*.swift")
fd -e png                     # -e = extension: filter by extension
fd -H '\.DS_Store' -x rm      # Include hidden files (-H); run command on match (-x = execute)

fzfFuzzy Finder

Etymology
fuzzy finder. "Fuzzy" means you can match targets with partial or non-consecutive characters.
bash
git branch | fzf | xargs git switch    # Interactively select a branch and switch to it

Then enable its Shell integration—add source <(fzf --zsh) to your ~/.zshrc—to search command history using Ctrl+R and select file paths with Ctrl+T. In terms of terminal productivity, this tool offers the highest return on investment.

xargs = converts standard input into arguments for the next command; an adapter for piping.

batSyntax Highlighting cat

Etymology
A double pun. It's cat (feline) → bat (winged mouse/feline counterpart), and also stands for "better cat."
bash
bat main.swift                # View files with syntax highlighting, line numbers, and Git changes

Quick Overview of Other Tools

ToolReplacesEtymologyIn a Nutshell
ezalsCommunity successor to exaeza -la --git: List files with Git status columns
jqJSON querySwiss Army knife for JSON: `curl -s url \
tldrmantoo long; didn't readExample-driven manual; a beginner's first lookup
ghOfficial GitHub CLIgh pr create: Open pull requests without leaving the terminal
htoptoph = author HishamInteractive and visual process monitor
treels -RTree structurestree -L 2: Print visual directory tree layout
hyperfineExtremely preciseBenchmark tool: hyperfine "cmd1" "cmd2" compares speed
xcbeautifyXcode + beautify`xcodebuild ... \

Appendix A: Naming Patterns Quick Reference

Abbreviations (Expand to Remember)

CommandFull NamePurpose
pwdprint working directoryWhere am I?
lslistList contents
cdchange directorySwitch directory
cp / mv / rmcopy / move / removeFile actions: duplicate / move / delete
lnlinkCreate links
wcword countCount lines/words
psprocess statusView running processes
df / dudisk free / disk usageDisk space available / consumed
manmanualSystem manual
chmod / chownchange mode / ownerChange permissions / owner
unameunix nameSystem information

Compounds and Acronyms

CommandOrigin
mkdirmake directory
greped command g/re/p (global / regular expression / print)
sedstream editor
awkInitials of authors: Aho, Weinberger, and Kernighan
tartape archive
sudosuperuser do
ssh / scpsecure shell / secure copy
curlclient for URLs
lsoflist open files
digdomain information groper
fzffuzzy finder
tldrtoo long; didn't read

Metaphors (Visual Mnemonics)

CommandMetaphorical Association
brew FamilyBrewery: formula (recipe), cask (large barrel), tap (faucet), cellar (basement storage), bottle (pre-compiled)
git stashHiding place: store away half-finished changes
git cherry-pickSelect cherry: apply a specific commit from another branch
git rebaseRe-base: transplant branch onto a new foundation
caffeinateCaffeine boost: keep the Mac awake
staplerStapler: attach notarization tickets directly onto app bundles
pingSonar echo sound
less"Less is more": improved version of more
batWinged cat (better cat)
daemon (-d suffix)Guardian spirit: background systems working silently

Universal Options and Suffixes

text
Options: -a all │ -l long format │ -h human-readable/help │ -R recursive │ -f force
         -v verbose │ -i interactive │ -c count │ -n number │ -p print/parents
Suffixes: -ctl = control (controller) │ -util = utility (toolbox) │ -d = daemon (background service)

Again, these are conventions, not strict standards, and exceptions are common: ls -r is reverse sorting (recursion is -R), and grep -c reports matching line count instead of matches count. When in doubt, man is the final word.


Appendix B: High-Frequency Power Combos

Once you master pipes, these command chains become second nature:

bash
# Force-terminate the process using port 3000
kill -9 $(lsof -ti :3000)

# Identify the 10 largest files or directories in the current folder
du -sh * | sort -rh | head -10

# Count total lines of Swift code (excluding dependency folders)
fd -e swift -E Pods -E .build | xargs wc -l | tail -1

# Trigger a spoken announcement when the build finishes
xcodebuild -scheme MyApp build | xcbeautify && say "Build complete"

# Copy the filenames modified in the last commit to the clipboard
git diff --name-only HEAD~1 HEAD | pbcopy   # Compare explicitly between the two commits, avoiding uncommitted changes

# Count the number of TODO comments left in the project
rg "TODO" | wc -l

# Reclaim development disk space (check size first, then delete)
du -sh ~/Library/Developer/Xcode/DerivedData
rm -rf ~/Library/Developer/Xcode/DerivedData
xcrun simctl delete unavailable

Do not attempt to memorize everything at once. Focus on weekly steps; 10 minutes of daily hands-on practice is far more effective than reading cover-to-cover:

  • Week 1: Chapters 0–2. Practice pwd, ls, cd, mkdir, cp, mv, rm, and open. Force yourself to use the Terminal instead of Finder for basic file operations.
  • Week 2: Chapter 3. Master grep and the pipe operator |. Start using history | grep <keyword> to find commands you ran earlier.
  • Week 3: Chapters 6–7. Install Homebrew. Make the core Git commands (status, add, commit, push, pull) muscle memory. Visualize the four Git zones.
  • Week 4: Chapters 4, 5, and 8. Refer to these chapters as needed. Install fzf, rg, and tldr from Chapter 10—use tldr first whenever you encounter an unfamiliar command.
  • Beyond: Chapter 9. Learn the Apple developer tools as you encounter signing, notarization, and building tasks. Revisit advanced Git commands (rebase, bisect, reflog) when you face complex repository scenarios.

The key to memorization is silently recalling the full name of a command before running it: print working directory, change directory, superuser do... Within two weeks, the abbreviations will become second nature in your fingertips.