CLI Tools Every Developer Should Know: curl, jq, grep, and More
Knowing your way around a handful of CLI tools lets you inspect a live API, search a 10 GB log file, or transform structured data in seconds — tasks that would take minutes through a GUI. The tools covered here are available on virtually every server and developer machine.
Published June 28, 2026The command line is not about memorizing flags. It is about composing small, focused tools into pipelines that solve problems too specific or too large for any GUI to handle. The Unix philosophy — each tool does one thing well and outputs text that another tool can process — means that learning five tools gives you something closer to twenty-five workflows.
curl: the HTTP Swiss Army knife
curl sends HTTP requests from the command line. It is the fastest way to test an API endpoint, inspect headers, or debug authentication flows without writing a script.
# GET request
curl https://api.example.com/users/42
# GET with headers (common for JWT auth)
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..." \
https://api.example.com/users/42
# POST with a JSON body
curl -X POST \
-H "Content-Type: application/json" \
-d '{"name": "Alice", "email": "[email protected]"}' \
https://api.example.com/users
# Show response headers (-I for HEAD only, -i to include headers with body)
curl -i https://api.example.com/users/42
# Follow redirects (-L), fail on HTTP errors (-f), silent mode (-s)
curl -sfL https://example.com/resource
# Save response to a file
curl -o output.json https://api.example.com/data
# Pass form data (application/x-www-form-urlencoded)
curl -X POST -d "username=alice&password=secret" https://example.com/login
# Verbose mode: shows request and response headers, TLS handshake
curl -v https://api.example.com/users/42
The -v flag is invaluable for debugging: it shows the full request headers your client sent, the server's response headers, and the TLS certificate chain — everything you need to diagnose an authentication failure or an unexpected redirect.
jq: process JSON on the command line
jq parses and transforms JSON. Piped from curl, it turns an unreadable blob of JSON into a readable, filterable, and extractable structure.
# Pretty-print JSON (. is the identity filter)
curl -s https://api.example.com/users | jq '.'
# Extract a single field
curl -s https://api.example.com/users/42 | jq '.name'
# Output: "Alice"
# Extract from an array
curl -s https://api.example.com/users | jq '.[0].email'
# Map over an array: extract one field from each element
curl -s https://api.example.com/users | jq '[.[] | .name]'
# Output: ["Alice", "Bob", "Carol"]
# Filter array by condition
curl -s https://api.example.com/users | jq '[.[] | select(.role == "admin")]'
# Construct a new object
curl -s https://api.example.com/users/42 | jq '{id: .id, name: .name}'
# Extract raw string without quotes (-r flag)
curl -s https://api.example.com/users/42 | jq -r '.name'
# Output: Alice (no surrounding quotes)
# Read from a file instead of stdin
jq '.users[] | .email' data.json
grep: search text with patterns
grep filters lines that match a pattern. It is the fastest way to search log files, find usages in code, or isolate errors in output.
# Basic search: find lines containing "error"
grep "error" app.log
# Case-insensitive (-i)
grep -i "error" app.log
# Show line numbers (-n)
grep -n "NullPointerException" app.log
# Invert match: lines that do NOT contain "debug" (-v)
grep -v "DEBUG" app.log
# Extended regex (-E) for alternation and quantifiers
grep -E "error|warning|critical" app.log
# Recursive search in a directory (-r), only .py files
grep -r "TODO" src/ --include="*.py"
# Count matching lines (-c)
grep -c "200 OK" access.log
# Show context: 3 lines before and after each match (-A after, -B before, -C both)
grep -C 3 "FATAL" app.log
# Highlight matches and show filename (-H)
grep -rH "deprecated_function" src/
awk: column extraction and simple transforms
awk processes text line by line, splitting each line into fields. It excels at extracting specific columns from structured output (logs, CSV, command output).
# Print the second field (default delimiter: whitespace)
# For a log line: "2026-06-28 14:32:01 INFO User 42 logged in"
# $1=2026-06-28, $2=14:32:01, $3=INFO, etc.
awk '{print $2}' app.log # print the timestamp column
# Print specific fields with a custom delimiter
awk -F',' '{print $1, $3}' data.csv # fields 1 and 3 from CSV
# Filter by condition: only lines where 3rd field is "ERROR"
awk '$3 == "ERROR" {print}' app.log
# Sum a numeric column (field 7, e.g. response time in ms)
awk '{sum += $7} END {print "Total ms:", sum}' access.log
# Count occurrences of each HTTP status code (field 9 in Apache log)
awk '{count[$9]++} END {for (code in count) print code, count[code]}' access.log | sort
Combining tools with pipes
The real power comes from piping the output of one tool into the next. Each tool does one step; together they do something neither could alone.
# Find the top 10 most common error messages in a log file:
grep "ERROR" app.log \ # 1. filter to error lines
| awk '{print $5}' \ # 2. extract the message field
| sort \ # 3. sort alphabetically (required before uniq)
| uniq -c \ # 4. count consecutive identical lines
| sort -rn \ # 5. sort by count descending
| head -10 # 6. show only the top 10
# Extract all unique IP addresses from an Nginx access log:
awk '{print $1}' /var/log/nginx/access.log | sort -u
# Find all endpoints returning 500, extract request URL, sort by frequency:
grep ' 500 ' access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -20
# Watch a file grow in real time (tail -f) and highlight errors:
tail -f app.log | grep --color "ERROR\|FATAL"
A few more tools worth knowing
sed performs find-and-replace on streams: sed 's/old/new/g' file.txt. Useful for bulk text transforms without opening an editor.
xargs converts stdin lines into command arguments, letting you apply a command to each item in a list: find . -name "*.tmp" | xargs rm.
wc -l counts lines: grep "ERROR" app.log | wc -l tells you exactly how many error lines matched.
cut extracts fixed-position columns: cut -d',' -f2 data.csv extracts the second comma-delimited field — simpler than awk for straightforward column extraction.
The investment in learning these tools pays off quickly because they work the same way everywhere — on your laptop, on a production server you SSH into, and inside a Docker container. They handle files too large for any text editor and run faster than most scripted alternatives.