Power Tools
grep, rg, sed, awk, find, fzf, jq, ssh.
The shell's real magic isn't any one command. It's gluing small tools together with pipes to build a one-liner that would take 50 lines in Python. This lesson is the toolbelt: searching, filtering, transforming, and reaching into remote machines.
grep and ripgrep: find text
grep searches files for lines matching a pattern. ripgrep (the rg command) is the modern, faster, smarter cousin that respects .gitignore by default. If you can install one tool today, install ripgrep.
# classic grep
grep "TODO" src/app.js
grep -r "TODO" src/ # recursive
grep -in "error" server.log # -i ignore case, -n line numbers
# ripgrep (faster, smarter defaults)
rg "TODO" # recursive by default, skips node_modules
rg "useState" -t ts # only TypeScript files
rg "fetch\(" -A 3 # show 3 lines after each match
rg -l "TODO" # list filenames only, no matchesrg "useState" src/
# src/components/Counter.tsx
# 3:import { useState } from "react";
# 7: const [count, setCount] = useState(0);
#
# src/hooks/useForm.ts
# 12: const [values, setValues] = useState(initial);sed: quick stream edits
sed reads a stream and applies edits. 90% of real-world use is just substitution: s/find/replace/.
# replace first occurrence per line
echo "hello world" | sed 's/world/there/'
# hello there
# replace all (the g flag)
echo "a a a" | sed 's/a/b/g'
# b b b
# edit a file in place (macOS needs the empty string after -i)
sed -i '' 's/localhost:3000/api.example.com/g' config.js
# delete lines matching a pattern
sed '/^#/d' config.txt # remove all lines starting with #awk: when your data is tabular
awk splits each line into fields and lets you do simple math or filtering. It's ancient and weird, but for tabular output it's unbeatable.
ps aux | awk '{print $2, $11}'
# PID COMMAND
# 1 /sbin/launchd
# 432 /usr/bin/ssh-agent
# 9821 node
# sum a column
du -k * | awk '{total += $1} END {print total/1024 " MB"}'
# 248.4 MB
# filter then format
df -h | awk '$5+0 > 80 {print $1, $5}' # show partitions over 80% fullfind: locate files by metadata
find . -name "*.log" # all .log files anywhere below
find . -type d -name "node_modules" # directories named node_modules
find . -size +100M # files larger than 100MB
find . -mtime -7 # modified in the last 7 days
find . -name "*.tmp" -delete # find and delete (be careful)
# combined with another command via -exec
find . -name "*.ts" -exec wc -l {} +rg --files respects .gitignore and runs in milliseconds where find can crawl for ages.jq: surgical JSON
jq is grep for JSON. Filter, extract, reshape, all from the command line. You will use this constantly when poking at HTTP APIs.
curl -s https://api.github.com/repos/microsoft/vscode | jq '.stargazers_count'
# 162847
# extract specific fields
curl -s https://api.github.com/users/torvalds/repos \
| jq '.[] | {name, stars: .stargazers_count}'
# { "name": "linux", "stars": 178432 }
# { "name": "subsurface", "stars": 2103 }
# ...
# pretty-print a local file
cat package.json | jq
# filter an array
jq '.dependencies | keys' package.json
# [ "next", "react", "react-dom" ]Pipes and redirection
This is the glue. A pipe | feeds one command's output into the next. Redirections (>, >>, 2>&1) send output to files instead of the screen.
# pipes
cat access.log | grep "POST" | wc -l # count POST requests
ps aux | grep node | awk '{print $2}' # PIDs of all node processes
# redirect stdout to a file (overwrite)
npm run build > build.log
# append instead of overwrite
echo "done" >> build.log
# stderr is a separate stream
npm run test 2> errors.log # only errors go to file
npm run test > all.log 2>&1 # combine stdout + stderr
npm run test &> all.log # shorthand in bash/zsh
# silence stderr entirely
some-noisy-command 2> /dev/null2>&1 means "redirect stderr to wherever stdout is currently going."Background jobs
Stick an & on the end of a command and the shell runs it in the background, immediately giving you back the prompt.
npm run dev &
# [1] 51932
jobs
# [1]+ Running npm run dev &
# bring it back to the foreground
fg %1
# suspend a running foreground job with Ctrl-Z, then push it to background
# (Ctrl-Z)
bg %1
# kill a background job
kill %1For long-running things (servers, log tails, build watchers) most devs actually prefer a terminal multiplexer like tmux or just opening a second tab. But knowing & and jobs is essential.
SSH: your remote shell
ssh opens a shell on a remote computer. The connection is encrypted. It's how you log into servers, deploy code, and run commands on the cloud.
ssh ada@server.example.com
# (you're now on the server)
# run one command and exit
ssh ada@server "df -h"
# specify a port
ssh -p 2222 ada@server.example.com
# copy files over ssh
scp local-file.txt ada@server:/var/www/
rsync -avz ./build/ ada@server:/var/www/site/ssh-keygen and ssh config
Typing your password every connection is annoying and less secure than keys. Generate a key once, copy the public half to the server, and you're passwordless.
# generate a modern Ed25519 key
ssh-keygen -t ed25519 -C "ada@laptop"
# saves ~/.ssh/id_ed25519 (private) and id_ed25519.pub (public)
# install your public key on the server
ssh-copy-id ada@server.example.com
# now this works without a password
ssh ada@server.example.comEven better: an ~/.ssh/config file gives connections nice nicknames and remembers all your flags.
Host prod
HostName server.example.com
User ada
Port 2222
IdentityFile ~/.ssh/id_ed25519
Host github.com
User git
IdentityFile ~/.ssh/id_ed25519_githubssh prod # uses everything from the config above
scp deploy.sh prod:/tmp/.pub file is the one you copy to servers. The non-.pub file stays on your machine, period. Leaking a private key is like leaving your house keys in a public park.A real-world pipeline
Imagine your server logs have lines like [2024-05-24T14:02:13] 200 GET /api/users 18ms. Show me the 10 slowest endpoints from the last hour:
tail -n 100000 access.log \
| rg "$(date -v-1H +%Y-%m-%dT%H)" \
| awk '{print $5, $4}' \
| sort -rn \
| head -10
# 1842ms GET /api/reports/heavy
# 1320ms POST /api/upload
# 980ms GET /api/users
# ...That's 5 small tools doing one thing each, pipelined. This is the terminal's real power: you assemble it.
Quick quiz
What does 2>&1 mean?
Recap
- grep / ripgrep find lines. sed rewrites them. awk handles columns. find locates files by metadata. jq surgically reshapes JSON.
- Pipes (
|) chain tools. Redirections (>,>>,2>&1) route output to files. &andjobsmanage background processes;fg/bgmove them between foreground and background.sshopens a shell on a remote server.ssh-keygen+~/.ssh/configmake it passwordless and ergonomic.