Modern CLI Tools - Street Ops¶
Practical workflows that combine modern tools for real DevOps tasks.
Workflow 1: Searching Repos Efficiently¶
Find a config value across a large monorepo¶
# Old way (slow, noisy)
grep -rn "replicas:" --include="*.yaml" .
# Modern way (fast, filtered, contextual)
rg "replicas:" -t yaml -C 2
# Shows 2 lines of context, only YAML files, respects .gitignore
# Even better: interactive
rg "replicas:" -t yaml -l | fzf --preview 'bat --color=always -H $(rg -n "replicas:" {} | head -1 | cut -d: -f1) {}'
# Lists matching files, preview shows the file with the match highlighted
Find where a function is defined vs used¶
# Definition (likely has "def " or "func " prefix)
rg "def error_handler" -t py
rg "func.*ErrorHandler" -t go
# All usages
rg "error_handler" -t py -l # Just file names
rg "error_handler" -t py -c # Count per file
Under the hood:
rg(ripgrep) is fast because it uses memory-mapped I/O, SIMD-accelerated regex (via theregexcrate), and parallelism by default. It also respects.gitignoreautomatically, skippingnode_modules/,vendor/, and build artifacts. This meansrgin a large repo is often 10-50x faster thangrep -r, not because of a better algorithm, but because it searches fewer files.
Find all TODOs and tech debt markers¶
Workflow 2: Log Triage Pipeline¶
Parse structured JSON logs¶
# Application emits JSON logs
# {"timestamp":"2024-01-15T10:23:45Z","level":"error","message":"connection refused","service":"api"}
# Filter errors only
cat app.log | jq 'select(.level == "error")'
# Count errors by service
cat app.log | jq -r 'select(.level == "error") | .service' | sort | uniq -c | sort -rn
# Extract error messages from the last hour
cat app.log | jq 'select(.level == "error" and .timestamp > "2024-01-15T09:00:00Z") | .message'
# Pretty table of recent errors
cat app.log | jq -r 'select(.level == "error") | [.timestamp, .service, .message] | @tsv' | column -t
Parse Kubernetes events¶
# Find all warning events, sorted by count
kubectl get events -A -o json | jq -r '.items[] | select(.type == "Warning") | [.count, .reason, .involvedObject.name, .message] | @tsv' | sort -rn | head -20
# Find recent crashloopbackoff events
kubectl get events -A -o json | jq '.items[] | select(.reason == "BackOff") | {name: .involvedObject.name, ns: .involvedObject.namespace, count, message}'
Systemd journal triage¶
# Errors from the last hour, service name highlighted
journalctl -p err --since "1 hour ago" --no-pager | rg "systemd|kernel|docker" --color=always
# Find the noisiest services
journalctl -p err --since today -o json | jq -r '._SYSTEMD_UNIT // "kernel"' | sort | uniq -c | sort -rn | head -10
Workflow 3: JSON Manipulation Patterns¶
jq Cheatsheet (Top 10 Patterns)¶
# 1. Pretty print
echo '{"a":1}' | jq '.'
# 2. Extract a field
kubectl get svc -o json | jq '.items[].metadata.name'
# 3. Filter by condition
kubectl get pods -o json | jq '.items[] | select(.status.phase != "Running")'
# 4. Transform to a new shape
kubectl get pods -o json | jq '.items[] | {name: .metadata.name, phase: .status.phase, restarts: .status.containerStatuses[0].restartCount}'
# 5. Count items
kubectl get pods -o json | jq '.items | length'
# 6. Sort by a field
kubectl get pods -o json | jq '.items | sort_by(.status.containerStatuses[0].restartCount) | reverse | .[0:5]'
# 7. Group by
kubectl get pods -o json | jq '[.items[] | {ns: .metadata.namespace, phase: .status.phase}] | group_by(.ns) | .[] | {namespace: .[0].ns, count: length}'
# 8. Raw output for scripting
kubectl get pods -o json | jq -r '.items[].metadata.name'
# 9. Multiple outputs per input
kubectl get pods -o json | jq -r '.items[] | "\(.metadata.name)\t\(.status.phase)"'
# 10. Slurp + combine
cat file1.json file2.json | jq -s '.[0] * .[1]' # Merge objects
yq for YAML (same syntax as jq)¶
# Read a value
yq '.metadata.name' deployment.yaml
# Update a value
yq '.spec.replicas = 3' deployment.yaml
# Add an annotation
yq '.metadata.annotations.updated = "2024-01-15"' deployment.yaml
# Merge YAML files
yq '. *= load("overrides.yaml")' base.yaml
# Convert YAML to JSON
yq -o json deployment.yaml
Gotcha: There are two different tools both called
yq: the Go version (mikefarah/yq, jq-like syntax) and the Python version (kislyuk/yq, wraps jq). They have incompatible syntax. The Go version is more common in DevOps workflows. Check which you have withyq --version. If recipes from the internet do not work, you likely have the wrongyq.
Workflow 4: Interactive Selection¶
Choose a pod to exec into¶
Choose a branch to checkout¶
Choose a docker container to follow logs¶
Choose a file to edit¶
Remember: fzf keybindings mnemonic: Ctrl-R (history), Ctrl-T (file picker), Alt-C (cd into directory). Add
source <(fzf --bash)to your.bashrcto get all three. These three keybindings replace hours of manual typing per week.
Workflow 5: Replacing Legacy Combos¶
| Legacy | Modern | Notes |
|---|---|---|
find . -name "*.py" \| xargs grep "import" |
rg "import" -t py |
Single command, faster |
grep -rl "old" . \| xargs sed -i 's/old/new/g' |
rg -l "old" \| xargs sed -i 's/old/new/g' |
rg is faster for the search phase |
find . -name "*.log" -mtime +7 -delete |
fd -e log --changed-before 7d -x rm |
More readable |
ls -la \| sort -k5 -rn \| head |
eza -la --sort size |
Built-in sorting |
du -sh * \| sort -rh \| head -20 |
dust |
Visual bar chart |
cd $(find . -type d \| fzf) |
zi |
Remembers frecency |
cat file \| less |
bat file |
Syntax highlighting |
diff file1 file2 |
delta file1 file2 |
Highlighted, word-level diffs |
Power One-Liners¶
Pipe viewer for progress on any pipeline¶
pv access.log | gzip > access.log.gz # compression progress
dd if=/dev/sda bs=4M | pv -s $(blockdev --getsize64 /dev/sda) | dd of=disk.img bs=4M # disk clone with ETA
tar cf - /data | pv -s $(du -sb /data | awk '{print $1}') | gzip > data.tgz # tar with progress
Breakdown: pv (pipe viewer) inserts into any pipeline and shows throughput, ETA, and progress bar. -s sets expected size for percentage/ETA calculation.
[!TIP] When to use: Any long-running pipeline where you need visibility — backups, transfers, compressions.
Quick Reference¶
- Cheatsheet: Modern-Cli