Modern CLI Tools¶
Reference guide for the new generation of command-line tools that replace classic Linux utilities. Faster, more user-friendly, and increasingly standard in DevOps workflows.
Why These Tools Matter¶
The classic Unix tools (find, grep, cat, ls, du, cd) work, but modern
replacements offer significant improvements:
- Speed: Multi-threaded, optimized for large codebases and modern hardware
- Defaults: Sensible defaults (recursive search, ignore .git, colorized output)
- UX: Better error messages, interactive features, visual output
- Integration: Work well together and with DevOps tools (kubectl, Terraform, etc.)
Installation¶
# Ubuntu/Debian (apt + cargo for some)
sudo apt install fd-find ripgrep fzf bat jq
# fd is installed as fdfind on Debian (alias it)
alias fd='fdfind'
# eza, zoxide, dust, delta (via cargo or direct download)
cargo install eza zoxide du-dust git-delta
# Or install everything via Homebrew (Linux + macOS)
brew install fd ripgrep fzf bat jq eza zoxide dust git-delta
# zoxide shell integration (add to .bashrc or .zshrc)
eval "$(zoxide init bash)" # or zsh, fish
Shell Configuration¶
Add to ~/.bashrc or ~/.zshrc:
# Modern tool aliases
alias ls='eza --icons --group-directories-first'
alias ll='eza -la --icons --group-directories-first --git'
alias lt='eza --tree --level=2 --icons'
alias cat='bat --paging=never'
alias less='bat'
alias du='dust'
alias find='fd'
alias grep='rg'
alias cd='z' # after zoxide init
# fzf configuration
export FZF_DEFAULT_COMMAND='fd --type f --hidden --follow --exclude .git'
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
export FZF_ALT_C_COMMAND='fd --type d --hidden --follow --exclude .git'
# fzf preview with bat
export FZF_CTRL_T_OPTS="--preview 'bat --color=always --style=numbers --line-range=:500 {}'"
# fzf shell integration
[ -f /usr/share/doc/fzf/examples/key-bindings.bash ] && source /usr/share/doc/fzf/examples/key-bindings.bash
[ -f /usr/share/doc/fzf/examples/completion.bash ] && source /usr/share/doc/fzf/examples/completion.bash
# delta as git pager
git config --global core.pager delta
git config --global interactive.diffFilter 'delta --color-only'
git config --global delta.navigate true
git config --global delta.side-by-side true
fd (find replacement)¶
Repo: sharkdp/fd | Replaces: find
fd is a fast, user-friendly alternative to find. It's 5-10x faster on large
directory trees, respects .gitignore by default, and uses regex patterns.
Side-by-Side: fd vs find¶
| Task | find | fd |
|---|---|---|
| Find files by name | find . -name "*.yaml" |
fd '\.yaml$' |
| Find files by name (case-insensitive) | find . -iname "*.yaml" |
fd -i '\.yaml$' |
| Find directories only | find . -type d -name config |
fd -t d config |
| Find files modified < 1 day | find . -mtime -1 |
fd --changed-within 1d |
| Find and delete | find . -name "*.tmp" -delete |
fd '\.tmp$' -X rm |
| Find files > 10MB | find . -size +10M |
fd -S +10M |
| Exclude directory | find . -path ./vendor -prune -o ... |
fd -E vendor |
| Find with exec | find . -name "*.sh" -exec chmod +x {} \; |
fd -e sh -x chmod +x |
Essential fd Patterns for DevOps¶
# Find all Kubernetes manifests
fd -e yaml -e yml . k8s/
# Find all Dockerfiles (any variation)
fd -g 'Dockerfile*'
# Find all Helm templates
fd -e tpl -e yaml . helm/templates/
# Find large files (disk cleanup)
fd -S +100M /
# Find files not in .gitignore (include hidden)
fd -H
# Find files including .gitignore'd ones
fd -I -H
# Find all shell scripts and check syntax
fd -e sh -x shellcheck
# Find recently changed config files
fd -e conf -e cfg --changed-within 1h /etc/
ripgrep / rg (grep replacement)¶
Repo: BurntSushi/ripgrep | Replaces: grep -r, ag, ack
ripgrep is the fastest code search tool. It's recursive by default, respects
.gitignore, and uses Rust's regex engine for speed.
Side-by-Side: rg vs grep¶
| Task | grep | rg |
|---|---|---|
| Search recursively | grep -r "pattern" . |
rg "pattern" |
| Case-insensitive | grep -ri "pattern" . |
rg -i "pattern" |
| Show only filenames | grep -rl "pattern" . |
rg -l "pattern" |
| Count matches per file | grep -rc "pattern" . |
rg -c "pattern" |
| Fixed string (no regex) | grep -rF "exact.string" . |
rg -F "exact.string" |
| Context lines | grep -rC3 "pattern" . |
rg -C3 "pattern" |
| Specific file type | grep -r --include="*.py" "pattern" . |
rg -t py "pattern" |
| Exclude directory | grep -r --exclude-dir=vendor "pat" . |
rg -g '!vendor' "pat" |
| Multiline match | Not supported well | rg -U "start.*\nend" |
| Replace (preview) | sed pipeline |
rg "old" -r "new" |
Essential rg Patterns for DevOps¶
# Find all TODO/FIXME in code
rg 'TODO|FIXME|HACK|XXX'
# Search Kubernetes manifests for a specific image
rg 'image:' -t yaml
# Find all environment variable references
rg '\$\{?\w+\}?' -t sh
# Find hardcoded IPs
rg '\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b'
# Search only in specific file types
rg -t tf 'aws_instance' # Terraform files
rg -t py 'import requests' # Python files
rg -t yaml 'kind: Deployment' # YAML files
# Find potential secrets (basic patterns)
rg -i '(password|secret|token|api.?key)\s*[:=]' --glob '!*.lock'
# Count matches by file type
rg 'func ' --type-list | head -20 # Show available types
rg -c 'func ' -t go # Count Go functions
# Search compressed files
rg -z "ERROR" /var/log/syslog.gz
# Replace across files (dry run with -r, apply with sed)
rg 'oldFunction' -l | xargs sed -i 's/oldFunction/newFunction/g'
# Show stats
rg "pattern" --stats
fzf (fuzzy finder)¶
Repo: junegunn/fzf | Purpose: Interactive filtering for any list
fzf is a general-purpose fuzzy finder that turns any list into an interactive,
searchable menu. It integrates with your shell and with other tools via piping.
Shell Integration¶
After installation, fzf adds three keybindings:
| Keybinding | Action | Equivalent |
|---|---|---|
Ctrl-R |
Fuzzy search command history | history \| fzf |
Ctrl-T |
Fuzzy search files, insert path | fd \| fzf |
Alt-C |
Fuzzy search directories, cd into | fd -t d \| fzf |
Essential fzf Patterns for DevOps¶
# Interactive file open in editor
vim $(fzf)
# Fuzzy search + preview with bat
fzf --preview 'bat --color=always --style=numbers {}'
# Pipe ripgrep into fzf for interactive code search
rg --line-number "pattern" | fzf --delimiter ':' --preview 'bat --color=always --highlight-line {2} {1}'
# Kubernetes: select a pod interactively
kubectl get pods -A | fzf | awk '{print $2, "-n", $1}' | xargs kubectl describe pod
# Kubernetes: tail logs of selected pod
kubectl get pods | fzf | awk '{print $1}' | xargs -I{} kubectl logs -f {}
# Docker: select and remove containers
docker ps -a | fzf -m | awk '{print $1}' | xargs docker rm
# Git: checkout branch interactively
git branch -a | fzf | sed 's/remotes\/origin\///' | xargs git checkout
# Git: interactive log browser
git log --oneline | fzf --preview 'git show {1}'
# SSH to a host from known_hosts / inventory
cat ~/.ssh/known_hosts | awk '{print $1}' | sort -u | fzf | xargs ssh
# Select and kill a process
ps aux | fzf | awk '{print $2}' | xargs kill
# Environment variable browser
env | fzf
# Ansible: select and run a playbook
fd -e yml . playbooks/ | fzf | xargs ansible-playbook
# Helm: select a release to inspect
helm list -A | fzf | awk '{print $1, "-n", $2}' | xargs helm status
Custom fzf Functions¶
# fkill - fuzzy process killer
fkill() {
local pid
pid=$(ps aux | fzf -m | awk '{print $2}')
if [ -n "$pid" ]; then
echo "$pid" | xargs kill -${1:-9}
fi
}
# fco - fuzzy git checkout
fco() {
local branch
branch=$(git branch -a | fzf --query="$1" | sed 's/remotes\/origin\///' | xargs)
[ -n "$branch" ] && git checkout "$branch"
}
# fpod - fuzzy kubectl pod selector, runs given command
fpod() {
local pod ns
read -r ns pod _ <<< "$(kubectl get pods -A | fzf | awk '{print $1, $2}')"
if [ -n "$pod" ]; then
kubectl "$@" "$pod" -n "$ns"
fi
}
# Usage: fpod logs -f
# fpod describe pod
# fpod exec -it -- bash
bat (cat replacement)¶
Repo: sharkdp/bat | Replaces: cat, less
bat is a cat clone with syntax highlighting, git integration (shows diff markers),
line numbers, and automatic paging.
Side-by-Side: bat vs cat¶
| Task | cat | bat |
|---|---|---|
| View file | cat file.py |
bat file.py |
| View with line numbers | cat -n file.py |
bat file.py (default) |
| View specific lines | sed -n '10,20p' file |
bat -r 10:20 file.py |
| Plain output (no style) | (default) | bat -pp file.py |
| Concatenate files | cat a.txt b.txt |
bat a.txt b.txt |
| Highlight specific line | Not possible | bat --highlight-line 42 f.py |
Essential bat Patterns for DevOps¶
# View YAML with highlighting
bat deployment.yaml
# View a specific line range (great for reading logs)
bat -r 100:150 /var/log/syslog
# Use as man pager (add to .bashrc)
export MANPAGER="sh -c 'col -bx | bat -l man -p'"
# Use as git diff pager (via delta, which builds on bat)
git diff | bat -l diff
# Preview in fzf
fzf --preview 'bat --color=always --style=numbers --line-range=:500 {}'
# View without paging (pipe-friendly, like cat)
bat -pp file.txt | wc -l
# Show all supported languages
bat --list-languages
# Highlight Kubernetes manifests
kubectl get deploy nginx -o yaml | bat -l yaml
# Highlight JSON output
curl -s https://api.example.com/health | bat -l json
# Side-by-side diff with bat highlighting (via delta)
diff file1.txt file2.txt | bat -l diff
jq (JSON processor)¶
Website: jqlang.github.io/jq | Purpose: Slice, filter, transform JSON
jq is the Swiss army knife for JSON processing. Essential for DevOps because
everything speaks JSON: APIs, kubectl, Terraform state, AWS CLI, container configs.
jq Cheatsheet: 10 Most-Used Expressions¶
# 1. Pretty-print JSON
echo '{"a":1,"b":2}' | jq .
# 2. Extract a field
kubectl get pod nginx -o json | jq '.status.phase'
# 3. Extract nested field
kubectl get pod nginx -o json | jq '.status.containerStatuses[0].ready'
# 4. Select from array
kubectl get pods -o json | jq '.items[] | .metadata.name'
# 5. Filter array elements
kubectl get pods -o json | jq '.items[] | select(.status.phase != "Running") | .metadata.name'
# 6. Multiple fields (construct object)
kubectl get nodes -o json | jq '.items[] | {name: .metadata.name, cpu: .status.capacity.cpu, mem: .status.capacity.memory}'
# 7. Map/transform
echo '[1,2,3]' | jq 'map(. * 2)'
# 8. Count items
kubectl get pods -o json | jq '.items | length'
# 9. Sort by field
kubectl get pods -o json | jq '.items | sort_by(.metadata.creationTimestamp) | .[].metadata.name'
# 10. Convert to CSV/TSV
kubectl get pods -o json | jq -r '.items[] | [.metadata.name, .status.phase, .status.podIP] | @tsv'
DevOps jq Patterns¶
# Kubernetes: Find pods not ready
kubectl get pods -A -o json | jq -r '
.items[]
| select(.status.containerStatuses[]?.ready == false)
| [.metadata.namespace, .metadata.name, .status.phase]
| @tsv'
# Kubernetes: Get resource requests/limits
kubectl get pods -o json | jq -r '
.items[]
| .metadata.name as $pod
| .spec.containers[]
| [$pod, .name, .resources.requests.cpu // "none", .resources.requests.memory // "none",
.resources.limits.cpu // "none", .resources.limits.memory // "none"]
| @tsv'
# Terraform: Parse state for resource addresses
terraform show -json | jq -r '.values.root_module.resources[] | [.type, .name, .values.id] | @tsv'
# AWS CLI: List EC2 instances
aws ec2 describe-instances | jq -r '
.Reservations[].Instances[]
| [.InstanceId, .InstanceType, .State.Name,
(.Tags[]? | select(.Key=="Name") | .Value) // "unnamed"]
| @tsv'
# Docker: Inspect container networking
docker inspect $(docker ps -q) | jq '.[].NetworkSettings.Networks | to_entries[] | {container: .key, ip: .value.IPAddress}'
# Helm: Parse values file
bat values.yaml | yq -o json | jq '.replicaCount, .image.repository, .image.tag'
# jq as a calculator/transformer
echo '{"prices": [10.5, 20.3, 15.8]}' | jq '.prices | add' # sum
echo '{"prices": [10.5, 20.3, 15.8]}' | jq '.prices | add / length' # average
echo '{"prices": [10.5, 20.3, 15.8]}' | jq '.prices | sort | reverse | .[0]' # max
# Merge two JSON files
jq -s '.[0] * .[1]' base.json override.json
# Update a field in-place
jq '.replicas = 5' config.json > config.json.tmp && mv config.json.tmp config.json
eza (ls replacement)¶
Repo: eza-community/eza | Replaces: ls, successor to exa
eza provides colorized file listings with tree view, git status integration,
icons, and file type indicators.
Side-by-Side: eza vs ls¶
| Task | ls | eza |
|---|---|---|
| List files | ls |
eza |
| Long format | ls -la |
eza -la --git |
| Sort by size | ls -lS |
eza -la --sort=size |
| Sort by modified time | ls -lt |
eza -la --sort=modified |
| Tree view (2 levels) | tree -L 2 |
eza --tree --level=2 |
| Show only directories | ls -d */ |
eza -D |
| Human-readable sizes | ls -lh |
eza -la (default) |
Essential eza Patterns for DevOps¶
# Project overview with tree + git status
eza --tree --level=2 --git --icons
# Find large files in a directory
eza -la --sort=size --reverse
# Show git-modified files
eza -la --git --git-ignore
# Quick Helm chart overview
eza --tree --level=3 --icons devops/helm/
# Kubernetes manifests listing with detail
eza -la --sort=name devops/k8s/
# Show file headers (extended attributes, permissions)
eza -la --header --group
# List only YAML files
eza --icons *.yaml *.yml
zoxide (cd replacement)¶
Repo: ajeetdsouza/zoxide | Replaces: cd, autojump, z
zoxide is a smarter cd that learns your habits. It tracks directory visit
frequency and recency (frecency) to let you jump to directories with partial names.
How It Works¶
1. You cd around normally, zoxide records every directory visit
2. Over time, frequently/recently visited directories get higher scores
3. Type `z partial-name` to jump to the best match
Side-by-Side: z vs cd¶
| Task | cd | zoxide |
|---|---|---|
| Go to specific path | cd /home/user/projects/grok |
z grok |
| Go to deeply nested dir | cd devops/ansible/roles/k3s_server/tasks |
z k3s_server |
| Go back | cd - |
z - |
| Interactive directory picker | Not possible | zi |
| List known directories | Not possible | zoxide query -l |
| Add directory manually | Not possible | zoxide add /path/to/dir |
Essential zoxide Patterns¶
# Jump to a project (from anywhere)
z grokdevops
# Jump to Ansible roles
z roles
# Jump to Helm templates
z templates
# Interactive mode (fuzzy search all known dirs)
zi
# Show top directories by score
zoxide query -ls | head -20
# Remove a stale directory from database
zoxide remove /old/deleted/path
dust (du replacement)¶
Repo: bootandy/dust | Replaces: du, ncdu
dust provides visual disk usage analysis with bar charts, sorted by size,
and understands the difference between apparent size and actual disk usage.
Side-by-Side: dust vs du¶
| Task | du | dust |
|---|---|---|
| Directory sizes | du -sh * |
dust |
| Sort by size | du -sh * \| sort -h |
dust (default) |
| Show depth | du -d 2 -h |
dust -d 2 |
| Specific directory | du -sh /var/log |
dust /var/log |
| Apparent size | du --apparent-size -sh * |
dust -s |
| Reverse sort (smallest) | du -sh * \| sort -h |
dust -r |
Essential dust Patterns for DevOps¶
# Quick disk usage overview
dust
# Check what's eating /var
dust /var
# Show only top 10 entries
dust -n 10
# Check container storage
dust /var/lib/containerd
# Show two levels deep
dust -d 2
# Compare apparent vs actual size (sparse files, compression)
dust -s /var/lib/docker
# Find what's using disk in a Kubernetes node
dust -d 2 /var/lib/kubelet
# Check log directory sizes
dust /var/log
delta (diff replacement)¶
Repo: dandavison/delta | Replaces: diff, git diff
delta provides syntax-highlighted diffs with side-by-side mode, word-level
highlighting, and line numbers. Best used as your git pager.
Git Integration¶
# Set delta as git pager (one-time setup)
git config --global core.pager delta
git config --global interactive.diffFilter 'delta --color-only'
git config --global delta.navigate true
git config --global delta.side-by-side true
git config --global delta.line-numbers true
# Now all git diff/log/show commands use delta automatically
git diff
git log -p
git show HEAD
git stash show -p
Standalone Usage¶
# Compare two files with highlighting
delta file1.py file2.py
# Side-by-side diff
delta --side-by-side file1.py file2.py
# Diff with line numbers
delta --line-numbers file1.py file2.py
# Compare command outputs
diff <(kubectl get deploy -o yaml) <(cat deploy.yaml) | delta
htop / btop (top replacement)¶
Repo: htop-dev/htop, aristocratos/btop | Replaces: top
htop¶
Interactive process viewer with tree mode, filtering, and mouse support.
# Launch htop
htop
# Show only processes for a user
htop -u deploy
# Show tree view on startup
htop -t
# Key shortcuts inside htop:
# F3 or / Search process
# F4 Filter by string
# F5 Tree view toggle
# F6 Sort by column
# F9 Send signal (kill)
# t Tree view
# H Hide user threads
# K Hide kernel threads
# p Show full command path
btop¶
More visual alternative with CPU, memory, disk, and network graphs.
# Launch btop
btop
# Key features:
# - CPU usage graph with per-core breakdown
# - Memory/swap usage with bar charts
# - Disk I/O graphs
# - Network bandwidth graphs
# - GPU monitoring (if supported)
# - Process tree with search/filter
httpie / xh (curl replacement)¶
Repo: httpie/cli, ducaale/xh | Replaces: curl for HTTP operations
Side-by-Side: http vs curl¶
| Task | curl | httpie |
|---|---|---|
| GET request | curl https://api.example.com |
http api.example.com |
| POST JSON | curl -X POST -H 'Content-Type: application/json' -d '{"key":"val"}' URL |
http POST URL key=val |
| Custom header | curl -H 'Authorization: Bearer tok' URL |
http URL Authorization:'Bearer tok' |
| Download file | curl -O URL |
http --download URL |
| Follow redirects | curl -L URL |
http --follow URL |
| Form data | curl -d 'user=admin' URL |
http -f POST URL user=admin |
| Verbose | curl -v URL |
http -v URL |
DevOps Patterns¶
# Check Kubernetes API health
http --verify=no GET https://localhost:6443/healthz Authorization:"Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
# Test a service endpoint
http :8080/health
# Post JSON to a webhook
http POST https://hooks.slack.com/services/xxx text="Deploy complete"
# Download and inspect Helm chart
http --download https://charts.example.com/myapp-1.0.0.tgz
yq (YAML processor)¶
Repo: mikefarah/yq | Purpose: jq syntax for YAML files
Essential for Kubernetes manifests, Helm values, Ansible vars, and any YAML config.
Essential yq Patterns for DevOps¶
# Read a field
yq '.metadata.name' deployment.yaml
# Read nested field
yq '.spec.template.spec.containers[0].image' deployment.yaml
# Update a field
yq -i '.spec.replicas = 5' deployment.yaml
# Add an annotation
yq -i '.metadata.annotations.updated = "2026-03-05"' deployment.yaml
# Merge YAML files (like Helm values overrides)
yq eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' values.yaml values-prod.yaml
# Convert YAML to JSON
yq -o json deployment.yaml
# Convert JSON to YAML
cat config.json | yq -P
# Extract all container images from a multi-doc YAML
yq -r '.. | .image? | select(.)' k8s-manifests.yaml | sort -u
# Kubernetes: List all resource kinds in a manifest
yq '.kind' *.yaml | sort -u
# Update Helm values programmatically
yq -i '.image.tag = "v2.0.0"' values.yaml
yq -i '.replicaCount = 3' values.yaml
procs (ps replacement)¶
Repo: dalance/procs | Replaces: ps aux
Colorized, searchable process viewer with tree view and TCP/UDP port display.
# Show all processes (replaces ps aux)
procs
# Search for a process
procs nginx
# Tree view
procs --tree
# Show TCP/UDP ports per process
procs --tcp --udp
# Watch mode (like top but for specific processes)
procs --watch nginx
# Sort by CPU usage
procs --sortd cpu
# Sort by memory
procs --sortd mem
tldr (man companion)¶
Repo: tldr-pages/tldr | Complements: man
Community-maintained simplified man pages with practical examples.
# Install
npm install -g tldr
# or
pip install tldr
# Usage
tldr tar
tldr curl
tldr kubectl
tldr rsync
tldr jq
tldr ssh-keygen
# Update the local cache
tldr --update
Tool Combinations: Power Workflows¶
The real power comes from combining these tools:
# Find files, fuzzy select, open in editor
fd -e py | fzf --preview 'bat --color=always {}' | xargs vim
# Search code, fuzzy filter results, preview context
rg --line-number "TODO" | fzf --delimiter ':' --preview 'bat --color=always --highlight-line {2} {1}'
# Find large files, sort, visualize
dust -d 3 / 2>/dev/null | head -30
# Kubernetes pod log search
kubectl get pods -A --no-headers | fzf | awk '{print $1, $2}' | xargs -n2 sh -c 'kubectl logs -n $0 $1' | rg "ERROR"
# Git: find commits touching a file, preview changes
git log --oneline --all -- '*.yaml' | fzf --preview 'git show {1} | delta'
# Find all YAML files with a specific key, edit the selected one
rg -l 'replicas:' -t yaml | fzf --preview 'bat --color=always {}' | xargs vim
# Disk cleanup: find largest directories, drill into the selected one
dust -d 1 | fzf | awk '{print $NF}' | xargs dust
# Search Ansible inventory for a host, SSH into it
rg 'ansible_host' inventory/ | fzf | rg -o '\d+\.\d+\.\d+\.\d+' | xargs ssh
# Multi-tool pipeline: find modified YAML, preview, select, apply
fd -e yaml --changed-within 1h | fzf --preview 'bat --color=always {}' | xargs kubectl apply -f