Linux Command Line: The Commands Worth Knowing Properly
A working set of shell commands for developers — searching, processing text, inspecting processes and diagnosing a server — with the flags that matter.
Table of contents
- Finding files
- Searching content
- Processing text
- Inspecting processes and resources
- Diagnosing a server that is misbehaving
- Composition is the actual skill
- Shell habits that pay off
- Frequently asked questions
- bash or zsh?
- Why does my sed command fail on macOS?
- How do I stop a runaway process I cannot identify?
- Should I learn vim?
- Related reading
- References
You do not need to know hundreds of commands. You need about twenty, properly, plus the composition rules that let them work together.
Finding files#
# By name, recursively
find . -name '*.ts' -not -path './node_modules/*'
# By modification time — changed in the last hour
find . -mmin -60 -type f
# By size — over 100 MB
find . -size +100M -type f
# Run a command on each result. -print0/-0 handles filenames with spaces.
find . -name '*.log' -print0 | xargs -0 rmThe -print0 | xargs -0 pairing is worth internalising: without it, a filename containing a space is treated as two arguments, and rm deletes the wrong thing.
Searching content#
# Recursive, case-insensitive, with line numbers
grep -rin 'TODO' src/
# Only list matching filenames
grep -rl 'deprecated' src/
# Show 3 lines of context around each match
grep -rn -C 3 'error' app.log
# Invert: lines that do NOT match
grep -v '^#' config.conf
# Fixed string, not a regex — much faster and no escaping needed
grep -rF 'a[0].b' src/ripgrep (rg) is a drop-in replacement that respects .gitignore and is dramatically faster on a large repo. If it is available, use it.
Processing text#
# Column 2 of a whitespace-delimited file
awk '{print $2}' access.log
# Sum a column
awk '{sum += $3} END {print sum}' data.txt
# Substitute in place (GNU sed; BSD/macOS needs -i '')
sed -i 's/old/new/g' file.txt
# Cut by delimiter
cut -d',' -f1,3 data.csv
# Count unique values, most frequent first — the classic log pipeline
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -20That last pipeline answers "which IPs hit us most?" and its shape generalises to almost any counting question. Note sort before uniq is mandatory — uniq only collapses adjacent duplicates.
Inspecting processes and resources#
# Interactive process view (htop is nicer if installed)
top
# Find a process by name
pgrep -a node
# Kill gracefully, then forcefully
kill <pid> # SIGTERM: asks the process to shut down
kill -9 <pid> # SIGKILL: cannot be caught or cleaned up after
# What is listening on port 3000?
lsof -i :3000
ss -tulpn | grep 3000 # Linux, faster
# Disk usage
df -h # by filesystem
du -sh ./* | sort -rh # by directory, largest firstReach for kill -9 only after kill has failed. SIGKILL gives the process no chance to flush buffers or release locks.
Diagnosing a server that is misbehaving#
A sequence that identifies most problems in a couple of minutes:
uptime # load average — is it CPU-bound?
free -h # is it out of memory?
df -h # is a disk full? (a very common cause)
journalctl -u myapp -n 100 --no-pager # recent service logs
journalctl -p err -since '1 hour ago' # errors across the system"Disk full" is the single most common cause of a service that has stopped working for no apparent reason — usually logs that were never rotated.
Composition is the actual skill#
# Every unique 4xx/5xx path in a log, by frequency
awk '$9 ~ /^[45]/ {print $7}' access.log | sort | uniq -c | sort -rn
# Total lines of TypeScript, excluding dependencies
find . -name '*.ts' -not -path './node_modules/*' | xargs wc -l | tail -1
# Watch a value change over time
watch -n 2 'ss -tn state established | wc -l'The pattern is always the same: one command produces lines, the next filters or transforms them.
Shell habits that pay off#
set -euo pipefail # in every script: exit on error, undefined var, or pipe failure
command || true # explicitly tolerate a failure
"$variable" # always quote — unquoted variables split on spaces
$(command) # prefer over backticks; nests correctlyset -euo pipefail is the single most valuable line in any shell script. Without -o pipefail, false | true succeeds, so a failing step in a pipeline is silently ignored.
Frequently asked questions#
bash or zsh?#
Either. zsh is the macOS default and has better completion; bash is more universally present. Write scripts with a #!/usr/bin/env bash shebang for portability.
Why does my sed command fail on macOS?#
BSD sed requires an argument to -i: sed -i '' 's/a/b/'. GNU sed does not. This is the most common cross-platform shell paper cut.
How do I stop a runaway process I cannot identify?#
ps aux --sort=-%cpu | head shows the biggest CPU consumers with their full command line, which usually identifies it immediately.
Should I learn vim?#
Enough to edit and exit a file on a server: i to insert, Escape, :wq to save and quit, :q! to quit without saving. Beyond that it is a preference, not a requirement.
Related reading#
References#
- GNU Coreutils manual
- ShellCheck — lints shell scripts for exactly the traps above