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:
- Abbreviation-based:
pwd= print working directory. Expand the abbreviation, and you will never forget it. - Compound-based:
mkdir= make directory. Portions of two words combined together. - Metaphor-based: Git's
stash(a hiding place), macOS'scaffeinate(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:
- 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.
- 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.
- 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.
- 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.
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:
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
└────────────────────── UsernameIn the examples that follow, the only part you need to type is what comes after the prompt.
Anatomy of a Command
ls -l -a ~/Desktop
│ │ │ └── Argument: What to operate on (here, the desktop directory)
│ │ └───── Option: Modifies behavior
│ └──────── Option: Modifies behavior
└─────────── Command: What to doThe structure rules are simple, and almost all commands follow them:
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:
| Option | Common Full Name | Meaning |
|---|---|---|
-a | all | Show all files (including hidden ones) |
-l | long | Long format (detailed information) |
-h | human-readable | Human-readable (displays 1048576 as 1M); when used alone, often stands for help |
-R | recursive | Recursive (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." |
-f | force | Force action, do not prompt for confirmation |
-v | verbose | Verbose mode (outputs detailed progress); sometimes stands for version |
-i | interactive | Interactive mode (asks before proceeding) |
-n | number | Count / quantity |
Troubleshooting: Three ways to help yourself when stuck
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.Etymologyman= manual.tldr= too long; didn't read—the name says it all: man pages are too long; just give me examples.
Keyboard Survival Skills
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
- Deleting via the command line bypasses the Trash. Once deleted with
rm, it is gone. - Be highly alert when you see `sudo`—it runs commands with administrative privileges, and the system will not stop you from breaking things.
- Do not copy and paste commands you do not understand, especially those containing
sudo,rm -rf, orcurl ... | sh. - When in doubt, check with
manortldrbefore 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:
/ ← 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
└── MyAppDouble-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?
Etymologyprint working directory.
pwd
# Output: /Users/jerry/ProjectslsWhat is here?
Etymologylist. In early Unix days, commands were compressed to two letters to minimize typing.
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 itAbout 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:
-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
Etymologychange directory.
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
| Notation | Meaning | Mnemonic |
|---|---|---|
/ | 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 directory | One dot = right here |
.. | Parent directory | Two dots = one level up |
- | Previous directory | Minus 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 Desktop → pwd to confirm location → cd - to jump back.
2. File Operations: Create, Copy, Move, and Delete
mkdirCreate Directory
Etymologymake directory.
mkdir Notes # Create a single directory
mkdir -p App/Sources/Views # -p = parents: Create parent directories if they don't existtouchCreate an Empty File
EtymologyLiterally "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.
touch README.mdcpCopy
Etymologycopy.
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 recursionmvMove (also handles Renaming)
Etymologymove. To the file system, "renaming" is just "moving a file to a new name in the same directory", so one command handles both.
mv draft.md final.md # Rename
mv final.md ~/Documents/ # MovermDelete (Danger Zone)
Etymologyremove.
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.
ln -sSymbolic Link (Shortcut)
Etymologylink;-s= symbolic, i.e., a "soft" link, similar to Finder's alias.
ln -s /path/to/real/folder ~/shortcuttar / zipArchiving and Compression
Etymologytar= 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.ziprefers to a zipper: pulling things together into a single package.
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 directoryThe 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)
EtymologyLiterally "open". It acts as a portal between the terminal and Finder/Apps. Linux users do not get this luxury.
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 Finder3. Viewing and Searching: Reading Files, Finding Content, and Chaining Commands
catPrint Entire File to Screen
Etymologyconcatenate. Its primary job is to concatenate multiple files end-to-end and output them.cat single_fileis merely a byproduct of this capability, yet it became the default way to view files.
cat notes.txt
cat a.txt b.txt > merged.txt # Core duty: Concatenate two fileslessPaginate Large Files
EtymologyA programmer's inside joke. Early pagination tools were namedmore("give me another page"). The improved version was namedlessas a play on the proverb "less is more."
less app.log
# Controls: Space to scroll down, /keyword to search, n for next match, q to exithead / tailView Start / End of a File
EtymologyLiterally the "head" and "tail".
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
Etymologyword count. However, its most common use is counting lines.
wc -l file.swift # -l = lines: Count number of linesgrepSearching Inside Content
EtymologyThe most interesting etymology in this guide. In the ancient editored, the instruction to "search globally (g) for a regular expression (re) and print (p) matching lines" was written asg/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.
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.).
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:
CommandA | CommandB # Pipe: Send output of A as input to B
Command > file # Redirection: Write output to file (overwrite)
Command >> file # Append output to fileEtymology|resembles a pipe. Data flows like water from left to right.
Let's experience the power of composition:
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
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 commandsEtymologyechoreturns whatever you call out.whichanswers "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.
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
Etymologyps= process status.toprefers to the top of the list—processes sorted by resource usage, so the most CPU-heavy ones sit at the top.
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 exitkillTerminating Processes
EtymologyDespite the aggressive name, its core mechanism is gentle: it sends a "signal" to a process. The default signal happens to be "please exit."-9is signal number 9, SIGKILL: no negotiations, terminate immediately.
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
Etymologysuperuser 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.
sudo xcode-select -s /Applications/Xcode.app
# Prompts for your login password; remembers authentication for a short grace periodKeep Safety Rule #2 in mind: the system does not stop root. You are responsible for what you run.
chmod / chownPermissions and Ownership
Etymologychange mode (change permission mode), change owner. Part of thech-prefix family.
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=executedf / duDisk Space Remaining / Used
Etymologydisk free and disk usage. Mirror commands:dfshows "how much is left," whiledushows "how much is used."
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 dieNaming Families: -ctl, -util, and -d
System commands in macOS/Unix often share three suffixes. Recognizing them helps you guess what an unfamiliar command does:
| Suffix | Meaning | Example |
|---|---|---|
-ctl | control: Controller for a service | launchctl (controls launchd), simctl (controls simulator) |
-util | utility: A toolbox for a specific domain | diskutil (disks), plutil (plists), textutil (documents) |
-d | daemon: A background service | launchd, sshd — background processes, not meant to be run directly by users |
"Daemon" (guardian spirit) is a classical, romantic Unix term for background services.launchctlis pronounced "launch control": it manages macOS's primary service manager,launchd.
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 details5. 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
Etymologypasteboard. This is Apple's official term for the clipboard. (If you write Apple apps, the corresponding APIs are indeedNSPasteboard/UIPasteboard—the command line and framework share the same lineage).
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
Etymologymetadata find. Spotlight works on top of a metadata index:findtraverses the file system in real-time, whilemdfindqueries 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 usefind.
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 typesdefaultsRead/Write User Preferences
EtymologyNamed after macOS's "user defaults database". TheUserDefaultsAPI you use inside apps interacts with this exact system—this command is its direct entry point.
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 filescaffeinatePrevent Sleep
EtymologyThe best command name in this chapter. caffeinate = "give caffeine to...". Give your Mac coffee, and it won't sleep.
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
Etymologyscriptable image processing system.
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 propertiesYou can wrap this in a loop to generate all App Icon sizes without opening any graphic design software.
Other Fun and Useful Commands
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)Etymologydittois an English word derived from Latin, meaning "same as above" or "copy exactly"—a more faithful duplicate thancp.saydoes 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.
EtymologyHomebrew = "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:
Term Literal Meaning Actual Meaning formula Recipe Installation script for a command-line tool cask Barrel Graphical applications (large packages) bottle Bottled beer Pre-compiled packages (no compiling needed, ready to drink) tap Beer tap Accessing third-party software sources (turn on the tap, beer flows) cellar Wine cellar The actual installation directory
Install Homebrew first (using the one-liner on brew.sh), and then:
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 issuesTwo advanced tips:
# 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 list7. 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:
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)
git config --global user.name "Jerry"
git config --global user.email "[email protected]"
git config --global init.defaultBranch mainconfig --globalwrites settings to~/.gitconfig, shared across all repositories on your system.
7.2 The Daily Loop: Get Through the Day with Five Commands
Etymologyclone(copy a remote repository);status(check current state);add(stage changes);push/pull(transfer data relative to your computer: push out, pull in).
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 remotefetchvspull: 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?
Etymologylog(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?!").
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 commitGood 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.HEADis your current location: the commit you currently have checked out.
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:
Etymologyrestore(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).
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 coordinatesRemember 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
Etymologyrebase= 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.
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:
.DS_Store
xcuserdata/
DerivedData/
.build/
Pods/
node_modules/
.env8. 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?
EtymologyOnomatopoeia 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.
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
Etymologyclient for URLs. It sends requests for any URL, making it the primary tool for testing APIs.
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:-Xspecifies method,-H= Header, and-d= data.
ssh / scpSecure Remote Login and File Transfer
Etymologysecure shell (encrypted remote shell) and secure copy (encrypted version of cp; argument order matches cp: source, then destination).
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?
Etymologylist 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.
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
Etymologydomain information groper. Also matches the verb "dig"—digging up DNS records.
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:
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder9. 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
EtymologyLiterally "Xcode selector". Decides which Xcode installation commands default to when you have stable and beta versions installed.
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 pathxcrunToolchain Portal
EtymologyXcode run. The Xcode toolchain contains hundreds of utilities (simctl,notarytool, etc.) that are not in the system PATH. They are executed throughxcrun. Readxcrun xxxas "run xxx from Xcode."
xcrun --find swift # Locate the tool's actual path
xcrun --show-sdk-path # Find the active SDK pathxcodebuildCommand-Line Build Tool
EtymologyXcode + build. The command-line equivalent of pressing ⌘B in Xcode.
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 projectThe output can be verbose; use xcbeautify (Chapter 10) to format it.
simctlSimulator Controller
Etymologysimulator control—a member of the-ctlsuffix family (Chapter 4).
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 settingscodesign / securitySignature Troubleshooting
Etymologycode + sign.securitymanages keychains, where certificates reside.
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 machineIf you encounter "no certificate found" errors, use the second command to check if the certificate exists in your keychain.
notarytool + staplerNotarization Pipeline
Etymologynotarize. 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.staplerattaches ("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.
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 approvalplutilPlist Utility
Etymologyproperty list utility.
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 programmaticallyCrash Analysis Duo
Etymologyatos= address to symbol: maps hex addresses in crash logs back to function names.dwarfdumpextracts 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.
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 numberswiftSwift Toolchain and Package Management
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 dependenciesRegular Maintenance
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 abbreviationrgpays homage to the Unix command tradition.
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 filesfdFaster find
EtymologyTruncating "find" gives "fd"—a direct representation of its product philosophy: shorter and simpler.
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
Etymologyfuzzy finder. "Fuzzy" means you can match targets with partial or non-consecutive characters.
git branch | fzf | xargs git switch # Interactively select a branch and switch to itThen 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
EtymologyA double pun. It's cat (feline) → bat (winged mouse/feline counterpart), and also stands for "better cat."
bat main.swift # View files with syntax highlighting, line numbers, and Git changesQuick Overview of Other Tools
| Tool | Replaces | Etymology | In a Nutshell |
|---|---|---|---|
eza | ls | Community successor to exa | eza -la --git: List files with Git status columns |
jq | — | JSON query | Swiss Army knife for JSON: `curl -s url \ |
tldr | man | too long; didn't read | Example-driven manual; a beginner's first lookup |
gh | — | Official GitHub CLI | gh pr create: Open pull requests without leaving the terminal |
htop | top | h = author Hisham | Interactive and visual process monitor |
tree | ls -R | Tree structures | tree -L 2: Print visual directory tree layout |
hyperfine | — | Extremely precise | Benchmark tool: hyperfine "cmd1" "cmd2" compares speed |
xcbeautify | — | Xcode + beautify | `xcodebuild ... \ |
Appendix A: Naming Patterns Quick Reference
Abbreviations (Expand to Remember)
| Command | Full Name | Purpose |
|---|---|---|
pwd | print working directory | Where am I? |
ls | list | List contents |
cd | change directory | Switch directory |
cp / mv / rm | copy / move / remove | File actions: duplicate / move / delete |
ln | link | Create links |
wc | word count | Count lines/words |
ps | process status | View running processes |
df / du | disk free / disk usage | Disk space available / consumed |
man | manual | System manual |
chmod / chown | change mode / owner | Change permissions / owner |
uname | unix name | System information |
Compounds and Acronyms
| Command | Origin |
|---|---|
mkdir | make directory |
grep | ed command g/re/p (global / regular expression / print) |
sed | stream editor |
awk | Initials of authors: Aho, Weinberger, and Kernighan |
tar | tape archive |
sudo | superuser do |
ssh / scp | secure shell / secure copy |
curl | client for URLs |
lsof | list open files |
dig | domain information groper |
fzf | fuzzy finder |
tldr | too long; didn't read |
Metaphors (Visual Mnemonics)
| Command | Metaphorical Association |
|---|---|
brew Family | Brewery: formula (recipe), cask (large barrel), tap (faucet), cellar (basement storage), bottle (pre-compiled) |
git stash | Hiding place: store away half-finished changes |
git cherry-pick | Select cherry: apply a specific commit from another branch |
git rebase | Re-base: transplant branch onto a new foundation |
caffeinate | Caffeine boost: keep the Mac awake |
stapler | Stapler: attach notarization tickets directly onto app bundles |
ping | Sonar echo sound |
less | "Less is more": improved version of more |
bat | Winged cat (better cat) |
daemon (-d suffix) | Guardian spirit: background systems working silently |
Universal Options and Suffixes
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:
# 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 unavailableAppendix C: Recommended Learning Path
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, andopen. Force yourself to use the Terminal instead of Finder for basic file operations. - Week 2: Chapter 3. Master
grepand the pipe operator|. Start usinghistory | 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, andtldrfrom Chapter 10—usetldrfirst 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.