Command-Line Productivity: The Shell Skills That Compound

The terminal fundamentals that pay back every day — pipes and composition, finding files and text fast, history tricks, and the handful of modern tools worth installing. Practical, not exhaustive.

Developer Workflow: Command-Line Productivity: The Shell Skills That Compound

Why the terminal still wins

Every few years something declares the command line obsolete, and every few years developers keep living in it. The reason is simple: the terminal composes. A GUI does exactly what its buttons allow; the shell lets you combine small tools into things nobody designed in advance. That composability is why a fluent terminal user often does in one line what takes ten clicks and three dialogs elsewhere — and the skill compounds, because every command you learn combines with every other one.

This isn’t an exhaustive reference. It’s the subset that earns its keep daily.

The one idea that matters: pipes

If you learn one thing, learn this. The pipe (|) sends one command’s output into the next command’s input. That’s the whole Unix philosophy — small tools that do one thing, chained together.

# Each stage feeds the next:
history | grep git | tail -20
#   ↑ all commands  ↑ only git ones  ↑ last 20

# Count how many times each command you run:
history | awk '{print $2}' | sort | uniq -c | sort -rn | head
#   ↑ history  ↑ 2nd word  ↑ group  ↑ count dupes  ↑ most-used first

That second one is worth running once — it shows your actual most-used commands, which tells you exactly what’s worth turning into an alias. The pattern sort | uniq -c | sort -rn (group, count, rank) appears constantly; internalize it and you can answer “what are the most common X?” for almost any text.

Finding things fast

Two tasks you do constantly: find files by name, find text inside files.

Find files:

# Classic, everywhere:
find . -name '*.test.js' -not -path '*/node_modules/*'

# Modern (fd) — faster, saner defaults, respects .gitignore:
fd '\.test\.js$'

Find text inside files:

# Classic:
grep -rn "TODO" src/

# Modern (ripgrep) — much faster, skips .gitignore and binaries automatically:
rg "TODO"
rg "createUser" --type js       # only JS files
rg -i "error" -A 3              # case-insensitive, 3 lines of context after

ripgrep (rg) is the single highest-return tool to install. It’s dramatically faster than grep on large codebases, ignores node_modules and .git by default, and the output is easier to read. Searching a big repo goes from “wait a second” to instant.

Typing cd ../../../projects/foo/src all day is a tax. Two fixes:

cd -        # jump back to the previous directory (toggle between two)

And install zoxide — it remembers directories you visit and jumps by fuzzy name:

z foo       # jumps to that project you were in yesterday, from anywhere
z src       # jumps to the src dir you use most

After a week of use it’s learned your habits and cd starts to feel archaic. Small thing, used fifty times a day.

History is a superpower

You’ve already typed most commands you need. Stop retyping them.

Ctrl + R          # reverse search — type any fragment, find the command
!!                # the entire last command
sudo !!           # re-run the last command with sudo (the classic)
!$                # the last argument of the previous command

That last one is quietly great:

mkdir -p src/components/ui
cd !$              # cd into src/components/ui without retyping it

But Ctrl+R is the one that changes your life. Ran a gnarly docker or ffmpeg command last week? Don’t reconstruct it — Ctrl+R, type “ffmpeg”, there it is. For even better history search, fzf turns Ctrl+R into a fuzzy finder over your whole history.

Aliases and functions: automate your repetition

Anything you type more than a few times a day should be shorter. Put these in your ~/.bashrc or ~/.zshrc:

# Aliases — simple substitutions
alias gs='git status'
alias gd='git diff'
alias gl='git log --oneline --graph --all'
alias ll='ls -lah'
alias ..='cd ..'

# Functions — when you need arguments
mkcd() { mkdir -p "$1" && cd "$1"; }   # make a dir and enter it
gcm() { git commit -m "$1"; }

The test for what deserves an alias: run that history | awk ... command from earlier. Your top ten are your best alias candidates. Don’t over-alias things you rarely use — you’ll forget them and they add clutter. Automate the genuinely frequent.

Chaining and conditionals

Combine commands with control over what runs when:

cmd1 && cmd2      # run cmd2 ONLY if cmd1 succeeded (exit 0)
cmd1 || cmd2      # run cmd2 ONLY if cmd1 failed
cmd1 ; cmd2       # run cmd2 regardless

# Practical: only start the server if the build succeeded
npm run build && npm start

# Practical: try the fast thing, fall back if it fails
npm ci || npm install

&& is the everyday one — it stops a chain the moment something fails, so you don’t run tests against a build that didn’t compile. This is also the backbone of one-line CI-style checks: npm run lint && npm test && npm run build.

Redirecting output

Control where output goes:

command > file.txt        # write stdout to a file (overwrites)
command >> file.txt       # append instead
command 2> errors.txt     # redirect stderr (errors) separately
command > out.txt 2>&1    # both stdout and stderr to one file
command 2>/dev/null       # discard errors (use sparingly — hides real ones)

The stdout-vs-stderr distinction trips people up: normal output and error output are separate streams. 2>&1 merges them, which you want when capturing a full log; 2>/dev/null throws errors away, which is occasionally handy and often a way to hide the very message you needed.

The modern toolkit worth installing

The classic Unix tools work everywhere and you should know them. But a few modern replacements are enough better to be worth installing on any machine you use daily:

Classic Modern Why bother
grep ripgrep (rg) Much faster, respects .gitignore, cleaner output
find fd Faster, simpler syntax, sane defaults
cat bat Syntax highlighting, line numbers
ls eza Colors, icons, git status inline
cd zoxide Learns your habits, jumps by fuzzy name
top htop/btop Actually readable process monitor

Install ripgrep, fd, and fzf at minimum — they’re the ones you’ll feel every day. The rest are nice-to-haves. Don’t rabbit-hole on configuring a perfect terminal setup for a week; install the high-value few and get back to work.

jq: for the JSON you deal with constantly

APIs return JSON; jq slices it from the command line:

curl -s https://api.example.com/users | jq '.[] | .name'   # just the names
curl -s https://api.example.com/user | jq '.email'          # one field
cat package.json | jq '.dependencies'                        # deps only

If you touch APIs, jq turns “paste this into a formatter and squint” into a one-line query. Worth an afternoon to learn the basics.

A caution against terminal perfectionism

There’s a failure mode where “improving my terminal setup” becomes the work instead of a means to it — endless dotfile tweaking, plugin managers, prompt themes. A beautiful terminal you spent a week configuring hasn’t shipped anything. Install the handful of tools that give real leverage, set up your most-used aliases, and stop. You can always add more when a specific friction shows up. The goal is doing your actual work faster, not curating a setup.

Why this compounds

Individually, none of these is dramatic — Ctrl+R saves ten seconds, an alias saves five. But you use the terminal hundreds of times a day, every day, for years. Ten seconds saved on something you do fifty times daily is real time, and more importantly it keeps you in flow instead of breaking concentration to retype a path or reconstruct a command.

The deeper payoff is composability: once pipes, search, and history are second nature, you start solving problems by combining tools on the fly — “find the files changed this week, that contain this string, and count them” becomes a reflex one-liner instead of a project. That fluency is what separates people who fight their tools from people whose tools disappear. Learn the fundamentals here, and everything else you learn in the terminal plugs into them.