A quiet revolution has been reshaping the Unix command line. One by one, the classic C-based utilities that have served us for decades are being replaced by Rust alternatives that are faster, safer, and dramatically more user-friendly. This is not about abandoning Unix philosophy. It is about implementing it better with modern tools and a language that eliminates entire categories of bugs.
The trend started with individual projects like ripgrep proving that a grep replacement written in Rust could be meaningfully faster than GNU grep while also having better defaults. That success inspired a wave of development. Today, you can replace nearly every core utility in your daily workflow with a Rust alternative, and the result is a terminal experience that is more productive, more visually informative, and more enjoyable to use.
This guide covers the essential Rust CLI tools, their advantages over the originals, practical configuration for each, and how to assemble them into a cohesive modern terminal setup.
Why Rust Is Winning the CLI Tool Space
Rust's dominance in modern CLI tooling is not accidental. Several properties of the language make it uniquely suited for this domain.
Performance without garbage collection. Rust produces binaries that match or exceed C performance. There is no runtime, no garbage collector, no JIT warmup. CLI tools need to start instantly and process data as fast as the disk can deliver it. Rust delivers this.
Memory safety by default. Classic C utilities have been a steady source of CVEs. Buffer overflows in basic tools like strings, file, and tar have led to real-world exploits. Rust's ownership model eliminates these bugs at compile time. For tools that process untrusted input (which is nearly all of them), this matters.
Fearless concurrency. Modern machines have many cores, but most classic Unix tools are single-threaded. Rust's type system makes parallelism safe and straightforward. Tools like ripgrep and fd use parallel directory traversal and search by default, producing dramatic speedups on large codebases.
Excellent CLI libraries. The clap crate provides argument parsing with auto-generated help, shell completions, and validation. crossterm and ratatui provide cross-platform terminal UI. indicatif provides progress bars. The ecosystem is mature and well-maintained.
Easy distribution. cargo install tool_name works everywhere. No autoconf, no system library dependencies, no version conflicts. A single static binary that runs on any Linux, macOS, or Windows machine.
File Viewing: bat (cat Replacement)
bat is a cat clone with syntax highlighting, Git integration, and automatic paging. Once you use it, plain cat feels like reading code on a monochrome monitor.
# Install
cargo install bat
# Or via package manager
brew install bat # macOS
sudo apt install bat # Debian/Ubuntu (binary name: batcat)
# Basic usage - syntax highlighting is automatic
bat src/main.rs
# Show specific lines
bat --line-range 10:20 config.yaml
# Show non-printable characters
bat --show-all data.bin
# Plain output (for piping, disables paging and decorations)
bat --plain --pager=never data.csv | head -20
# Use as a man page colorizer
export MANPAGER="sh -c 'col -bx | bat -l man -p'"
# Diff integration
bat --diff old_file.py new_file.py
Configuration lives in ~/.config/bat/config:
# ~/.config/bat/config
--theme="Catppuccin Mocha"
--style="numbers,changes,header,grid"
--italic-text=always
--map-syntax "*.conf:INI"
--map-syntax ".env*:Dotenv"
--map-syntax "*.dockerfile:Dockerfile"
bat integrates with Git to show modification markers in the gutter. Lines that have been added, modified, or removed relative to the Git index get colored markers. This turns simple file viewing into an instant code review tool.
Set alias cat='bat --paging=never' in your shell configuration for a seamless replacement that preserves pipe compatibility.
File Listing: eza (ls Replacement)
eza (formerly exa) is a modern replacement for ls with Git status, icons, tree view, and sensible color defaults. It is actively maintained and has become the standard recommendation after exa was archived.
# Install
cargo install eza
# Basic listing with icons and Git status
eza --icons --git
# Long format with headers
eza -lh --icons --git --header
# Tree view with depth limit
eza --tree --level=3 --icons
# Sort by modification time, newest first
eza -l --sort=modified --reverse
# Show only directories
eza -D --icons
# Grid view for wide terminals
eza --grid --icons
# Comprehensive alias
alias ll='eza -lah --icons --git --header --group-directories-first'
alias lt='eza --tree --level=3 --icons --git'
alias la='eza -a --icons --git'
eza's Git integration shows the status of each file directly in the listing:
Permissions Size User Date Modified Git Name
drwxr-xr-x - nick 21 May 10:30 -I src/
.rw-r--r-- 4.2k nick 21 May 09:15 M README.md
.rw-r--r-- 890 nick 20 May 14:22 -- Cargo.toml
.rw-r--r-- 2.1k nick 21 May 10:30 N new_module.rs
The M indicates a modified file, N indicates a new untracked file, and -- indicates a clean file. This visibility into Git state at the filesystem level eliminates the constant back-and-forth between ls and git status.
File Finding: fd (find Replacement)
fd is a fast, user-friendly alternative to find. Its defaults match what you actually want 95% of the time: regex patterns, .gitignore awareness, smart case sensitivity, and parallel execution.
# Install
cargo install fd-find
# Find files by name (regex by default)
fd "\.rs$"
# Case-insensitive search
fd -i readme
# Find by extension
fd -e py
# Find and execute a command
fd -e log --exec gzip {}
# Parallel execution with all results
fd -e test.js --exec-batch prettier --write
# Exclude directories
fd -E node_modules -E target "config"
# Find only directories
fd --type d "test"
# Find files modified in the last 24 hours
fd --changed-within 1d
# Show results with full details
fd -e rs --exec-batch eza -l
# Find files larger than 10MB
fd --size +10m
# Find empty files
fd --type f --type empty
Performance comparison on a large monorepo (100,000 files):
# GNU find
time find . -name "*.rs" -type f
# real 0m0.520s
# fd
time fd -e rs
# real 0m0.087s
# fd is ~6x faster due to parallel directory traversal
# and automatic .gitignore filtering (skips target/, node_modules/, etc.)
fd's .gitignore awareness is its killer feature for development work. In a typical project, find returns thousands of results from node_modules, target, .git, and build artifacts. fd skips all of these by default, showing only the files you care about.
Content Searching: ripgrep (grep Replacement)
ripgrep (rg) is the tool that arguably started the Rust CLI revolution. It is consistently the fastest code search tool available, with smart defaults that make it immediately productive.
# Install
cargo install ripgrep
# Basic search (recursive by default, respects .gitignore)
rg "fn main"
# Search specific file types
rg -t rust "async fn"
rg -t py "import torch"
# Search with context lines
rg -C 3 "TODO|FIXME|HACK"
# Count matches per file
rg -c "unwrap()"
# Replace text (preview)
rg "old_function" --replace "new_function"
# Search compressed files
rg -z "error" logs.gz
# Fixed strings (no regex interpretation)
rg -F "user.name?.first"
# Multiline search
rg -U "struct.*\{[^}]*name.*\}"
# Search hidden files and ignored files
rg --hidden --no-ignore "SECRET_KEY"
# Output as JSON for programmatic use
rg --json "pattern" | python3 process_results.py
# Files that do NOT match
rg --files-without-match "Copyright" --type rust
ripgrep uses finite automata for regex matching, which means it has predictable O(n) performance on all inputs. Pathological regex patterns that cause catastrophic backtracking in PCRE-based tools run in linear time with ripgrep.
Configuration via ~/.config/ripgrep/config:
--smart-case
--hidden
--glob=!.git
--glob=!node_modules
--glob=!target
--colors=match:fg:magenta
--colors=match:style:bold
Disk Usage: dust (du Replacement)
dust provides an instant visual overview of disk usage with a bar chart interface that makes large directories immediately obvious.
# Install
cargo install du-dust
# Basic usage (shows visual bars)
dust
# Limit depth
dust -d 2
# Show apparent size (not disk usage)
dust -s
# Reverse sort (smallest first)
dust -r
# Ignore specific directories
dust -X node_modules -X .git
# Show only N items
dust -n 15
# Show full file paths
dust -p
# Analyze a specific directory
dust -d 3 /var/log
Sample output:
4.2G ┌── node_modules │████████████████████████████ │ 52%
1.8G ├── target │████████████ │ 22%
890M ├── dist │██████ │ 11%
420M ├── .git │███ │ 5%
380M ├── data │██ │ 5%
180M ├── assets │█ │ 2%
92M ├── src │ │ 1%
45M ├── docs │ │ 1%
8.0G ┌── . │████████████████████████████████│ 100%
This visual output answers the question "where is my disk space going?" in a single glance, replacing the typical du -sh * | sort -h pipeline.
Process Viewing: procs (ps Replacement)
procs replaces ps with a colored, feature-rich process viewer that includes Docker container awareness and tree view.
# Install
cargo install procs
# Basic process list
procs
# Search by process name
procs firefox
# Tree view
procs --tree
# Watch mode (refresh every 2 seconds)
procs --watch
# Show specific columns
procs --insert TcpPort,Docker
# Sort by memory usage
procs --sortd mem
# Show only processes by current user
procs --or "user=$(whoami)"
procs automatically detects Docker containers and shows which container a process belongs to. It also maps TCP/UDP ports to processes without requiring root privileges, replacing the common lsof -i or ss -tlnp patterns.
System Monitoring: bottom (top Replacement)
bottom (btm) is a cross-platform system monitor with a rich TUI that includes CPU, memory, network, disk, temperature, and process widgets.
# Install
cargo install bottom
# Launch with default layout
btm
# Launch with specific refresh rate
btm --rate 500
# Basic mode (simpler, top-like interface)
btm --basic
# Battery widget
btm --battery
# Configuration file
btm --config ~/.config/bottom/bottom.toml
Configuration in ~/.config/bottom/bottom.toml:
[flags]
rate = 1000
dot_marker = false
temperature_type = "c"
color = "gruvbox"
[colors]
table_header_color = "LightBlue"
widget_title_color = "Gray"
[[row]]
[[row.child]]
type = "cpu"
[[row]]
ratio = 2
[[row.child]]
type = "mem"
[[row.child]]
type = "net"
[[row]]
[[row.child]]
type = "proc"
default = true
Keyboard shortcuts within bottom: dd kills a process, / searches, t toggles tree mode, Tab cycles widget focus. It feels like a modern IDE for system monitoring.
Diffing: difftastic (diff Replacement)
difftastic understands code structure. Instead of comparing lines, it parses files into syntax trees and compares the trees. The result is diffs that show what actually changed semantically, not just which lines were modified.
# Install
cargo install difftastic
# Compare two files
difft old.py new.py
# Use as git diff tool
git config --global diff.tool difftastic
git config --global difftool.difftastic.cmd 'difft "$LOCAL" "$REMOTE"'
git config --global difftool.prompt false
# Use inline with git diff
export GIT_EXTERNAL_DIFF=difft
git diff HEAD~1
# Limit display width
difft --display inline old.rs new.rs
# Color output control
difft --color always file_a.go file_b.go
difftastic supports over 50 programming languages. When you move a function from one place in a file to another, difftastic shows it as a move rather than a deletion plus an addition. When you rename a variable throughout a function, it highlights just the rename, not every line the variable appears on.
This structural understanding makes code review dramatically more efficient. The signal-to-noise ratio in diffs improves by an order of magnitude for refactoring commits.
Benchmarking: hyperfine
hyperfine is a command-line benchmarking tool that provides statistical analysis, warmup runs, parameter sweeps, and export to multiple formats.
# Install
cargo install hyperfine
# Basic benchmark
hyperfine 'fd -e py'
# Compare two commands
hyperfine 'find . -name "*.py"' 'fd -e py'
# With warmup runs
hyperfine --warmup 5 'rg pattern large_file.txt'
# Parameter sweep
hyperfine --parameter-scan threads 1 8 'parallel -j {threads} process_file ::: *.dat'
# Export results
hyperfine --export-markdown results.md 'command1' 'command2'
hyperfine --export-json results.json 'command1' 'command2'
# Set minimum number of runs
hyperfine --min-runs 20 'my_program'
# Prepare and cleanup commands
hyperfine \
--prepare 'sync; echo 3 | sudo tee /proc/sys/vm/drop_caches' \
'grep -r pattern /usr/src/linux' \
'rg pattern /usr/src/linux'
# Shell selection
hyperfine --shell=none './my_binary --flag'
Sample output:
Benchmark 1: find . -name "*.py" -type f
Time (mean +/- sd): 512.3 ms +/- 24.1 ms [User: 89.2 ms, System: 421.1 ms]
Range (min ... max): 478.9 ms ... 556.7 ms 10 runs
Benchmark 2: fd -e py
Time (mean +/- sd): 86.4 ms +/- 5.3 ms [User: 152.8 ms, System: 298.4 ms]
Range (min ... max): 79.1 ms ... 95.2 ms 34 runs
Summary
fd -e py ran 5.93 +/- 0.46 times faster than find . -name "*.py" -type f
Shell Enhancement: starship, zoxide, atuin
Three tools that transform the shell itself.
Starship is a cross-shell prompt that is fast, minimal, and infinitely configurable:
# Install
curl -sS https://starship.rs/install.sh | sh
# Add to shell
echo 'eval "$(starship init bash)"' >> ~/.bashrc
echo 'eval "$(starship init zsh)"' >> ~/.zshrc
Configuration in ~/.config/starship.toml:
[character]
success_symbol = "[>](bold green)"
error_symbol = "[>](bold red)"
[directory]
truncation_length = 3
truncation_symbol = ".../"
[git_branch]
symbol = " "
[rust]
symbol = " "
[python]
symbol = " "
[nodejs]
symbol = " "
[docker_context]
symbol = " "
zoxide is a smarter cd that learns your habits:
# Install
cargo install zoxide
# Initialize
eval "$(zoxide init bash)"
# Usage
z projects # Jump to most-used directory matching "projects"
z src test # Jump to directory matching both "src" and "test"
zi # Interactive selection with fzf
atuin replaces shell history with a SQLite database, sync, and fuzzy search:
# Install
cargo install atuin
# Initialize
eval "$(atuin init bash)"
# Search history (Ctrl+R replacement)
# Shows full-text fuzzy search with timestamps,
# directory context, and exit codes
# Import existing history
atuin import auto
# Search with filters
atuin search --after "2026-05-01" --exit 0 "docker"
Git Workflow: lazygit and delta
lazygit provides a full terminal UI for Git operations:
# Install
go install github.com/jesseduffield/lazygit@latest
# Or
brew install lazygit
# Launch in current repo
lazygit
# Key bindings within lazygit:
# Space - stage/unstage file
# c - commit
# p - push
# P - pull
# b - branch operations
# m - merge
# r - rebase interactively
delta is a syntax-highlighting pager for Git, diff, and grep output:
# Install
cargo install git-delta
# Configure in ~/.gitconfig
# [core]
# pager = delta
# [interactive]
# diffFilter = delta --color-only
# [delta]
# navigate = true
# side-by-side = true
# line-numbers = true
# syntax-theme = Catppuccin-mocha
Building a Modern Terminal Setup
Here is a complete shell configuration that integrates all of these tools:
# ~/.bashrc or ~/.zshrc - Modern Rust CLI toolkit
# Core replacements
alias cat='bat --paging=never'
alias ls='eza --icons --group-directories-first'
alias ll='eza -lah --icons --git --header --group-directories-first'
alias lt='eza --tree --level=3 --icons --git'
alias la='eza -a --icons --git'
alias find='fd'
alias grep='rg'
alias du='dust'
alias ps='procs'
alias top='btm'
alias diff='difft'
# Git enhancements
alias lg='lazygit'
export GIT_EXTERNAL_DIFF=difft
# Shell enhancements
eval "$(starship init bash)"
eval "$(zoxide init bash)"
eval "$(atuin init bash)"
# bat as man pager
export MANPAGER="sh -c 'col -bx | bat -l man -p'"
# ripgrep config
export RIPGREP_CONFIG_PATH="$HOME/.config/ripgrep/config"
# fzf integration with fd
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'
Install everything in one command:
cargo install bat eza fd-find ripgrep du-dust procs bottom difftastic hyperfine zoxide atuin git-delta starship
Performance Comparison: Rust vs C Implementations
The performance advantages of Rust CLI tools come from three sources: better defaults (ignoring irrelevant files), parallelism, and modern algorithms. Here are representative benchmarks on a 200,000-file monorepo:
| Task | Classic Tool | Rust Tool | Speedup |
|---|---|---|---|
| Find files by extension | find 520ms |
fd 87ms |
6.0x |
| Recursive text search | grep -r 4.2s |
rg 0.31s |
13.5x |
| Disk usage analysis | du -sh 1.8s |
dust 0.9s |
2.0x |
| File listing (large dir) | ls -la 340ms |
eza -la 290ms |
1.2x |
The biggest speedups come from search tools (ripgrep, fd) because they combine parallelism with intelligent file filtering. The smaller speedups in simpler tools (eza vs ls) reflect that the original tools were already well-optimized for their simpler task.
Note that some benchmarks are not apples-to-apples comparisons. fd is faster than find partly because it skips .gitignore-matched files by default. When you force fd to search everything (fd --no-ignore --hidden), the gap narrows. The point is that the default behavior of Rust tools matches what developers actually want, which makes them faster in practice even when the raw throughput is similar.
These tools represent a genuine improvement in the daily developer experience. They are not experimental. They are stable, well-maintained, and used in production by organizations ranging from startups to major technology companies. If you are still using the classic coreutils, try the Rust alternatives for a week. Most people never go back.