Sign in with Google

How to Use the Command Line (macOS and Linux)

Navigation, files, pipes, PATH, and the keyboard habits that make the terminal fast.

beginner10 min read
dev-setupcommand-lineterminalmacoslinux

The terminal is the one tool where copying commands off the internet works often enough to stop you from ever learning it. Then a tutorial says to "add it to your PATH" or "edit your bashrc" on a machine running zsh, and there is nothing to copy. This is a tour of the parts that recur: reading an unfamiliar command, moving around, plumbing output between programs, and the keyboard habits that separate slow from fast.
iWhat you need
A Mac or a Linux machine, and the Terminal app — on macOS press Command+Space and type "terminal". Nothing to install. Windows readers should install WSL first; everything below then applies inside it.
The window is a terminal. The program running inside it, reading what you type and deciding what to run, is a shell. They are separate things, which is why two people can use the same Terminal app and get different behavior.
macOS
$echo $SHELL
That prints your login shell — most likely /bin/zsh or /bin/bash. Since macOS Catalina, new Mac accounts default to zsh; most Linux distributions still default to bash. That single fork explains most contradictory advice you will read: a bash guide says to edit ~/.bashrc, and on a modern Mac that file is never read, so the instruction silently does nothing. Everything below works in both shells; where they differ, it says so.
Every command has the same three parts, and naming them lets you read commands you have never seen. In ls -la ~/Projects, ls is the program, -la is two flags bundled together, and ~/Projects is the argument. Single-letter flags take one dash and combine; word-length flags take two, as in --all. Two ways to look them up:
macOS
$man ls
That opens the manual page in a pager — arrows or Space to scroll, / to search, q to quit. The faster option is ls --help, a short summary. On Linux that works nearly everywhere; on macOS many core utilities are BSD versions that answer --help with a usage error, so man is the reliable one on a Mac.
The shell is always "in" one directory, and most commands act on that directory unless you say otherwise. Three commands cover all of navigation.

Navigation

  1. 1pwd — print working directory. Answers "where am I?", the first thing to check when a command behaves unexpectedly.
  2. 2ls -la — list everything in long format, including dotfiles. The -a is the important half: config files starting with a dot are hidden from a plain ls.
  3. 3cd ~/Projects/my-app — change directory. Run cd with no argument and you land in your home directory.
Paths come in two kinds. An absolute path starts at the filesystem root with a slash — /Users/you/Projects/my-app — and means the same thing from anywhere. A relative path is read from where you are, so src/index.js means "inside the src folder here". Three shorthands do a lot of work: ~ is your home directory, . is here, and .. is the parent. So cd ../.. is "up two levels".

The four you will use daily

  1. 1mkdir -p projects/my-app/src — make a directory. The -p flag creates every missing parent and stays quiet if it already exists, which is why scripts use it.
  2. 2touch notes.md — create an empty file, or update the timestamp on an existing one.
  3. 3cp notes.md notes-backup.md — copy a file. Copying a directory needs -r: cp -r src src-backup.
  4. 4mv notes.md docs/notes.md — move a file. There is no rename command; renaming is moving to a new name in the same place.
mv and cp overwrite without asking
If the destination already exists, both replace it silently. Add -i for interactive mode and they ask first — worth aliasing permanently, which the profile section below shows how to do.
rm notes.md deletes a file. Not to the Trash — the bytes are unlinked and there is no undo, no confirmation, and no recovery beyond a real backup. rm -r old-project deletes a directory and everything in it, and rm -rf adds "do not stop to ask about anything."
The rm -rf typo that costs an afternoon
rm -rf is unforgiving about spaces. rm -rf ~/tmp/old deletes one folder; rm -rf ~/tmp /old — one stray space — deletes two things, one of which you did not name. Read the whole line before pressing Return, and prefer rm -ri while the habit is new.
sudo runs one command as the root user, which can write anywhere on the machine. Reach for it when a command genuinely needs system-wide access, like a package manager writing to /usr/local — not because a command failed with a permission error, since that error is usually telling you something true. Avoid sudo pip install and sudo npm install -g in particular: they scatter root-owned files through your home directory that break confusingly weeks later. Use a version manager under your own account instead.

Four ways to look at a file

  1. 1cat config.json — dump the whole file to the screen. Fine for short files, unhelpful for a 4,000-line log.
  2. 2less server.log — open it in a scrollable pager. Arrows and Space to move, / to search, q to quit. The right default for anything long.
  3. 3head -n 20 data.csv — the first 20 lines. Ideal for checking a CSV's column headers.
  4. 4tail -f server.log — the last lines, then new ones as they are written. This is how you watch a running server; Ctrl+C stops it.
Two commands, and the distinction is worth keeping straight: find searches for files by name, grep searches inside files for text.
Linux
$find . -name "*.log"
Linux
$grep -rn "TODO" .
The first walks the current directory tree and prints every path ending in .log. Quote the pattern, or the shell expands the asterisk before find sees it. The second searches recursively for TODO: -r descends into subdirectories and -n prints line numbers, which is what makes the output useful — src/app.js:42 tells you where to go.
Every command has an input, an output, and a separate channel for errors. Pipes and redirection reconnect those channels, and thinking of them as pipework rather than syntax makes them stick. Four symbols:

The plumbing

  1. 1The pipe, a vertical bar, sends one command's output into the next command's input: ls -la | grep .json lists files, then keeps only lines mentioning .json.
  2. 2A single right angle bracket redirects output into a file, replacing whatever was there: ls -la > files.txt.
  3. 3Two right angle brackets append instead of replacing — how you build up a log rather than overwrite it each run.
  4. 42>&1 merges the error channel into the output channel. Without it, a piped command's errors bypass your pipe and land on screen.
Chaining is a related idea with one important difference. mkdir build; cd build runs both no matter what — the semicolon just means "then". mkdir build && cd build runs the second only if the first succeeded. Use the second form for anything where step two would be wrong after step one failed, which is most things.
Environment variables are named values every program you launch can read. You read one with a dollar sign in front of its name, and the one that matters most is PATH.
macOS
$echo $PATH
That prints directories separated by colons. When you type python3, the shell walks that list in order and runs the first match — the entire explanation for "command not found" on a program you installed, and for having two versions where the wrong one wins. which python3 tells you which is winning. Setting a variable is export EDITOR=nano, which lasts until you close the window; anything permanent goes in your profile.
Your profile is a script the shell runs every time it starts. For zsh it is ~/.zshrc; for bash on Linux, ~/.bashrc. On macOS, bash reads ~/.bash_profile for login shells — the other half of why copied instructions land in files nothing reads. Check echo $SHELL, then edit the matching file.
bash
# Shorthands for things you type constantly
alias ll="ls -la"
alias ..="cd .."
alias gs="git status"

# Ask before clobbering a file
alias cp="cp -i"
alias mv="mv -i"

# Put your own scripts ahead of system ones
export PATH="$HOME/bin:$PATH"

A few lines worth adding to ~/.zshrc

Note the shape of that last line: it puts $HOME/bin first, then re-includes the old PATH. Leave off the :$PATH and you replace the list rather than extending it, at which point almost nothing is found. Changes apply only to new shells, so reload this one:
macOS
$source ~/.zshrc
Nobody fast is typing full paths. The gap is almost entirely these habits, and each takes a day to internalize.

Learn these six

  1. 1Tab completes file and directory names. Press it constantly; press it twice to list possibilities when a name is ambiguous. You also never typo a path again.
  2. 2Up and down arrows walk through your command history, so re-running the last command is one keystroke.
  3. 3Ctrl+R searches history backwards — type any fragment of an old command and it appears. The highest-value shortcut here. Return runs it, an arrow key edits it first.
  4. 4Ctrl+C interrupts whatever is running — how you stop a dev server or a hanging command.
  5. 5Ctrl+A jumps to the start of the line and Ctrl+E to the end, beating the arrow key across a long command.
  6. 6clear, or Ctrl+L, wipes the screen without touching anything running.

Symptom, cause, fix

  1. 1"command not found" for something you installed. It is not in your PATH, or the profile adding it was never reloaded. Run echo $PATH, then source your profile.
  2. 2You edited ~/.bashrc on a Mac and nothing happened. Your shell is zsh; move the lines to ~/.zshrc.
  3. 3"Permission denied" writing to a file you own. Check ls -la for owner and mode. If a past sudo command created it, fix ownership rather than adding more sudo.
  4. 4"No such file or directory" for a file you can see. You are in a different directory than you think, or the name has a space and needs quoting.
  5. 5A command printed nothing. Many Unix tools succeed silently by design — echo $? shows the exit code, where 0 means success.
  6. 6The terminal stopped responding to typing. You pressed Ctrl+S, which pauses output. Ctrl+Q resumes.
  7. 7A pipe misses the error you needed. Errors travel on a separate channel — add 2>&1 before the pipe.
The terminal is the surface everything else sits on, so the next steps are the tools you drive from it. How to Use Git is the highest-value one and reuses everything above. How to Set Up SSH Keys for GitHub puts the dotfile editing and permissions to immediate use, and How to Install and Use Homebrew on a Mac is how you install most things on macOS. Still choosing what to write? The Programming roadmap starts from fundamentals rather than a tool list.