Why Learn CLI Tools?
GUI tools are intuitive, but CLI tools are overwhelmingly superior in terms of automation, pipeline composition, and speed. Using a cooking analogy, GUI is like a microwave (convenient but limited), while CLI is like a knife and cutting board (once you’re skilled, you can prepare any dish).
This post covers four CLI tools that immediately boost day-to-day development productivity.
jq: The Swiss Army Knife for JSON
jq is a tool for parsing, filtering, and transforming JSON on the command line. It’s essential when analyzing API responses or processing JSON logs.
Installation
# macOS
brew install jq
# Ubuntu/Debian
sudo apt install jq
# Windows (scoop)
scoop install jq
Basic Usage
# Pretty print JSON
echo '{"name":"Alice","age":30}' | jq '.'
# Output:
# {
# "name": "Alice",
# "age": 30
# }
# Extract a specific field
echo '{"name":"Alice","age":30,"city":"Seoul"}' | jq '.name'
# Output: "Alice"
# Output without quotes (-r option)
echo '{"name":"Alice"}' | jq -r '.name'
# Output: Alice
# Filter arrays
echo '[{"name":"Alice","age":30},{"name":"Bob","age":25}]' | jq '.[] | select(.age > 27)'
# Output: {"name":"Alice","age":30}
# Transform into a new structure
echo '[{"first":"Alice","last":"Smith"},{"first":"Bob","last":"Jones"}]' | \
jq '[.[] | {full_name: (.first + " " + .last), initial: .last}]'
# Output:
# [
# { "full_name": "Alice Smith", "initial": "Smith" },
# { "full_name": "Bob Jones", "initial": "Jones" }
# ]
Practical Example: Analyzing API Responses
# Extract repository info from the GitHub API
curl -s "https://api.github.com/users/torvalds/repos?per_page=5" | \
jq '[.[] | {name: .name, stars: .stargazers_count, language: .language}]' | \
jq 'sort_by(-.stars)'
# Output:
# [
# { "name": "linux", "stars": 180000, "language": "C" },
# ...
# ]
# Extract only errors from JSON logs
cat app.log | jq -r 'select(.level == "error") | "\(.timestamp) \(.message)"'
# Output: 2026-04-07T10:00:01 Database connection failed
# Access deeply nested fields
echo '{"data":{"users":[{"id":1,"profile":{"email":"a@b.com"}}]}}' | \
jq '.data.users[0].profile.email'
# Output: "a@b.com"
HTTPie: A Modern Alternative to curl
HTTPie is a human-readable HTTP client. It lets you test APIs with more intuitive syntax than curl.
Installation
# macOS
brew install httpie
# pip (all platforms)
pip install httpie
# Ubuntu
sudo apt install httpie
Basic Usage
# GET request (protocol and method can be omitted)
http httpbin.org/get
# Output: (auto-colored + formatted JSON response)
# POST request + JSON body (= separator for key-value pairs)
http POST httpbin.org/post name=Alice age:=30 active:=true
# name=value → string
# age:=30 → number (: prefix for JSON types)
# active:=true → boolean
# Add headers (: separator)
http GET api.example.com/users \
Authorization:"Bearer eyJhbGci..." \
Accept:application/json
# File upload
http --form POST api.example.com/upload file@./document.pdf
# Form data submission
http --form POST api.example.com/login username=admin password=1234
Comparison with curl
# POST request with curl
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-H "Authorization: Bearer token123" \
-d '{"name":"Alice","age":30}'
# Same request with HTTPie (much more concise)
http POST api.example.com/users \
name=Alice age:=30 \
Authorization:"Bearer token123"
| Feature | curl | HTTPie |
|---|---|---|
| JSON formatting | Manual (| jq) | Automatic |
| Color output | None | Automatic |
| JSON body | -d '{"key":"val"}' | key=val |
| Add headers | -H "Key: Val" | Key:Val |
| File upload | -F "file=@path" | file@path |
Session Feature
# Save session — persist cookies/headers
http --session=myapi POST api.example.com/login username=admin password=1234
# Login cookies are saved to the session
# Reuse session in subsequent requests (auth info included automatically)
http --session=myapi GET api.example.com/profile
# Cookies are sent automatically
fzf: Fuzzy Finder
fzf is a tool that enables real-time fuzzy search on any list. Find files, history, branches, and anything else quickly.
Installation
# macOS
brew install fzf
$(brew --prefix)/opt/fzf/install # Set up key bindings
# Ubuntu
sudo apt install fzf
# Install directly from Git
git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install
Basic Usage
# Search for files in the current directory
fzf
# Type: "comp"
# Matches: src/components/Button.tsx, docker-compose.yml, ...
# Use arrow keys to select, Enter to confirm
# Combine with pipes
cat package.json | jq -r '.dependencies | keys[]' | fzf
# Fuzzy search through npm package list
# File search with preview
fzf --preview 'cat {}'
# Preview selected file contents on the right
# Select multiple items (Tab to select, Enter to confirm)
fzf --multi
Shell Integration (Key Feature)
# Key bindings automatically configured during fzf installation
# Ctrl+R — fuzzy search through command history (most used!)
# Before: history | grep docker → slow
# With fzf: Ctrl+R → type "docker" → instant find
# Ctrl+T — fuzzy search files/directories in current directory
# vim Ctrl+T → select file → open in vim
# Alt+C — fuzzy search directories then cd
# Alt+C → "comp" → cd to src/components/
Combining with Git
# Fuzzy search git branches then checkout
git branch -a | fzf | xargs git checkout
# Search commits in git log
git log --oneline | fzf --preview 'git show {1}'
# Preview diff based on commit hash
# Select changed files to add
git status -s | fzf --multi | awk '{print $2}' | xargs git add
# Register as a shell function (~/.bashrc or ~/.zshrc)
# Fuzzy git branch checkout
gbf() {
local branch
branch=$(git branch -a | sed 's/^..//' | sed 's|remotes/origin/||' | sort -u | fzf)
if [ -n "$branch" ]; then
git checkout "$branch"
fi
}
ripgrep (rg): An Ultra-Fast grep Alternative
ripgrep is a grep alternative optimized for code search. It automatically respects .gitignore, recursively searches by default, and is 2-5x faster than grep.
Installation
# macOS
brew install ripgrep
# Ubuntu
sudo apt install ripgrep
# Windows (scoop)
scoop install ripgrep
# cargo (Rust)
cargo install ripgrep
Basic Usage
# Recursive search in current directory (default behavior)
rg "TODO"
# Output:
# src/app.ts:42: // TODO: Add error handling
# src/utils.ts:15: // TODO: Implement caching
# Case-insensitive search
rg -i "error"
# Filter by file type
rg "import" --type ts # TypeScript files only
rg "def " --type py # Python files only
rg "SELECT" --type sql # SQL files only
# Search in a specific directory
rg "useState" src/components/
# Regex search
rg "console\.(log|warn|error)" --type ts
# Find console.log, console.warn, and console.error
# Output only matching filenames
rg -l "TODO"
# Output:
# src/app.ts
# src/utils.ts
Advanced Usage
# Show context lines (2 lines above and below)
rg "error" -C 2
# Exclude specific patterns
rg "import" --type ts --glob '!**/*.test.ts' # Exclude test files
# JSON output (for combining with other tools)
rg "TODO" --json | jq 'select(.type == "match") | .data.lines.text'
# Count matches per file
rg -c "console.log" --type ts | sort -t: -k2 -rn
# Output: (sorted by number of console.log occurrences)
# src/debug.ts:15
# src/app.ts:8
# src/utils.ts:3
# Replace (preview only, does not modify files)
rg "old_function" --replace "new_function"
# Speed comparison with grep (on large projects)
# grep -r "pattern" . → about 3 seconds
# rg "pattern" → about 0.3 seconds (10x faster)
fzf + ripgrep Combo
# Search with ripgrep + select with fzf + open in editor
rg --line-number "TODO" | fzf --delimiter : --preview 'cat -n {1} | head -{2}' | \
awk -F: '{print "+" $2, $1}' | xargs code -g
# Fuzzy search TODO lines, then open the selected line in VS Code
# Interactive ripgrep (real-time search in fzf)
rg_fzf() {
rg --line-number --color=always "$1" | \
fzf --ansi --delimiter : \
--preview 'cat -n {1}' \
--preview-window 'right:60%'
}
# Usage: rg_fzf "pattern"
Tool Combination Cheat Sheet
# Extract specific fields from API response then fuzzy select
http GET api.example.com/users | jq -r '.[].name' | fzf
# Search error patterns in log files then check context
rg "ERROR" logs/ -C 3 | fzf --ansi
# Search processes then kill
ps aux | fzf | awk '{print $2}' | xargs kill
# Find JSON files + search contents
rg -t json "api_key" | fzf
Installation Summary
| Tool | macOS | Ubuntu | Windows | Purpose |
|---|---|---|---|---|
| jq | brew install jq | apt install jq | scoop install jq | JSON processing |
| HTTPie | brew install httpie | apt install httpie | pip install httpie | HTTP client |
| fzf | brew install fzf | apt install fzf | scoop install fzf | Fuzzy search |
| ripgrep | brew install ripgrep | apt install ripgrep | scoop install ripgrep | Code search |
Practical Tips
- Start with
Ctrl+R(fzf history search): This alone dramatically improves terminal productivity. No more retyping long commands. jqis essential for API testing: Usecurl ... | jq '.data'to instantly parse API responses and view specific fields.- Use
rginstead ofgrep -r: It automatically respects.gitignore, skippingnode_modulesanddistfolders, and is much faster. - Combine tools: Connecting tools with pipes (
|) combines each tool’s strengths. Combos likerg | fzf,curl | jq | fzfare extremely powerful. - Create shell functions/aliases: Register frequently used combinations as functions in
~/.bashrcor~/.zshrcto perform complex tasks with a single command. - Install bat alongside these tools: Using
batinstead ofcatgives you syntax-highlighted file contents, making fzf previews much more useful.