webdev.complete
💻 The Terminal
🛠️Dev Toolbelt
Lesson 47 of 117
25 min

Shell Basics

Navigation, file ops, pipes, redirection, env vars.

The terminal looks intimidating from the outside and then you spend a week with it and realize it's just a chat window where the other side happens to be your computer. Every senior dev lives here. By the end of this lesson you'll know the dozen commands that cover 90% of what they do.

Where am I? pwd, cd, ls

A shell session is always "in" some directory, called the working directory. Three commands are your compass:

bash
pwd
# /Users/ada/projects/website

ls
# index.html  src  package.json  README.md

cd src
pwd
# /Users/ada/projects/website/src

cd ..        # go up one level
cd ~         # go to your home directory
cd -         # go back to the previous directory

lson its own shows a tidy list. The flag combo you'll type a thousand times is ls -la: long format, including hidden files (the ones that start with a dot).

bash
ls -la
# total 32
# drwxr-xr-x  6 ada  staff   192 May 24 10:11 .
# drwxr-xr-x  9 ada  staff   288 May 24 09:55 ..
# -rw-r--r--  1 ada  staff    34 May 24 10:00 .env
# -rw-r--r--  1 ada  staff   220 May 24 10:11 .gitignore
# drwxr-xr-x  5 ada  staff   160 May 24 10:08 src
# -rw-r--r--  1 ada  staff   512 May 24 10:11 package.json

The leftmost column is the permissions string. d means directory, then three sets of rwxfor owner, group, and everyone. You'll learn to skim it.

Dotfiles are just files
Files that start with . are hidden by convention only. They contain config (.zshrc, .gitignore, .env). There's no magic, just a filename starting with a dot.

Moving stuff around: cp, mv, rm, mkdir

bash
mkdir notes               # create a directory
mkdir -p a/b/c            # create nested dirs in one shot

cp index.html backup.html         # copy file
cp -r src/ src-backup/            # copy directory recursively

mv old.txt new.txt                # rename
mv draft.md notes/                # move into a directory

rm old.log                        # delete a file
rm -r build/                      # delete a directory and contents
rm -rf is forever
There's no Trash with rm. Once it's gone, it's gone. And rm -rf / will try to delete everything on your computer. Never paste an rmcommand you don't fully understand.

Reading files: cat, less

catdumps a file's contents to the terminal. Great for short files, awful for huge ones (you'll scroll forever).

bash
cat package.json
# {
#   "name": "website",
#   "version": "1.0.0",
#   "scripts": { "dev": "next dev" }
# }

For big files use less. It pages through one screen at a time. Arrows to scroll, / to search, q to quit.

bash
less server.log
# (use arrows, page-down, /pattern to search, q to quit)

Environment variables and $PATH

Environment variables are named values your shell knows about. Programs read them to pick up config (API keys, locales, paths). Set, read, list:

bash
export NAME="Ada"          # set for this session and children
echo $NAME                 # Ada

env                        # list all variables
env | grep PATH            # filter to PATH-related ones

The most important variable is $PATH. It's a list of directories the shell searches when you type a command. When you run git status, your shell looks through every directory in $PATH until it finds an executable named git.

bash
echo $PATH
# /opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin

which git
# /opt/homebrew/bin/git

which -a node
# /Users/ada/.nvm/versions/node/v22.0.0/bin/node
# /opt/homebrew/bin/node       (an older one earlier in PATH wins)
'command not found' is usually a PATH issue
If a tool you just installed isn't recognized, 9 times out of 10 its directory isn't in $PATH. Add it to your shell config.

Your shell config: .zshrc / .bashrc

Every time you open a new terminal, the shell runs a startup script. That's where you set $PATH, define aliases, and tweak the prompt. macOS defaults to zsh, so the file is ~/.zshrc. Linux often defaults to bash and ~/.bashrc.

~/.zshrc
# add Homebrew to PATH
export PATH="/opt/homebrew/bin:$PATH"

# a couple of useful aliases
alias ll="ls -la"
alias gs="git status"
alias ..="cd .."

# a prompt with the current directory and git branch
export PS1="%~ %# "

After editing, either open a new terminal or run source ~/.zshrc to apply changes to your current session.

history: rerun and search past commands

bash
history | tail -20
#  998  git status
#  999  npm run dev
# 1000  history | tail -20

!!                # re-run the last command
!999              # re-run command 999
sudo !!           # re-run last with sudo
^old^new          # re-run last, replacing 'old' with 'new'

Press Ctrl-R and start typing. The shell does a fuzzy search through your history. This is one of those tricks that, once you know it, you use 30 times a day.

Modern upgrades worth knowing

The classics above are universal. But a few modern tools dramatically speed up your workflow:

  • zoxide - a smarter cd. Learns the directories you visit and jumps to them by partial name. z web beams you to ~/code/personal/web-development.
  • fzf - fuzzy finder. Press Ctrl-R with fzf installed and history becomes interactive. Pipe anything into fzf to pick from it.
  • eza / bat - prettier ls and cat with icons and syntax highlighting.
  • starship - a fast, customizable prompt that shows git status, runtime versions, and more.
Install one thing this week
If you do nothing else, install fzf and learn Ctrl-R. It pays for itself in a single afternoon of debugging.

Quick quiz

Quiz1 / 3

What does ls -la show that ls does not?

Recap

  • pwd, cd, ls -la are your navigation core.
  • cp, mv, rm, mkdir move and reshape files. rm -rf is permanent.
  • $PATH is the list of directories your shell searches for commands.
  • Customize ~/.zshrc or ~/.bashrc for aliases and PATH tweaks.
  • Ctrl-R searches your history. Install fzf to supercharge it.