Skip to content

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