Replacing CLI tools with modern alternatives
25 August 2025
Over the last decade plus, I've replaced a lot of Unix CLI staples I use each day with modern alternatives, mostly written in Rust and Go. This process has been gradual over the years. I decided to take stock of where things are in 2025.
grep → ripgrep
Let's start with the big one. I use grep all the time. Unfortunately, both GNU
grep and BSD grep struggle with larger codebases and are quite fiddly to work
with. My use cases are dominated by source code, so git grep was a step into
the right direction, but that of course only works inside Git repositories.
I first replaced grep with ack! by Andy Lester around 2008. It offered much better defaults than either of the standard tools. Searching only source code and recursively by default was already a huge step up. But speed was still an issue in larger codebases back then.
From ack! I moved on to ag, The Silver Searcher by Geoff Greer in 2011. It started as clone of ack!, but with 5-10x faster performance. Ag added support for Pthreads in 2012 and wrote an excellent post about the experience -- I wish more tools did this.
By now, Rust was gathering steam and heading for the 1.0 release. A ton of
developers were caught by the hype wave and started dabbling in systems
programming. Andrew Gallant wrote the regex crate
for Rust, which probably inspired the birth of
ripgrep. It's even faster than any of
the previously mentioned tools, without sacrificing any of the good defaults and
ease of use. The blog post introducing
ripgrep offers a great overview of exactly how
this was achieved.
I think ripgrep is approaching the limits of how fast a grep utility can be while remaining broadly useful by default.
ls → eza
ls is one of those commands you run hundreds of times in a day without batting
an eye. The ls provided by GNU coreutils offers some modern allowances such as
supporting color, but once I came across exa, I
realized there was much more to ask for from a modern ls replacement.
As is often the case with utilities like this, the original author became
unreachable over time and a fork was born. I'm actually using the actively
maintained eza right now.
ls is so deeply ingrained into my fingers that I could never switch to writing
exa or eza, so I quickly aliased ls to eza directly.
cd → zoxide
After ls, it's only logical to continue with cd. How does one even improve
on cd, which is supposed to be quite limited in scope? By keeping a track of
which directories you've been into and matching against that history.
That is pretty much what autojump offers,
and that was my first cd replacement. However, it is written in Python, so
when Zoxide appeared on the scene offering the same functionality it was an easy
decision to upgrade.
Just as with ls, I'm completely unable to switch from cd, even to the
shorter z. Thus I've aliased cd to z directly.
cat → bat
When you're spelunking around in a terminal, it's often useful to take a quick
peek inside one text file or another. The cat provided by GNU coreutils is
very plain by design, and of course there is nothing wrong with staying small
and focused -- but occasionally one wishes that for source code, we could have
stuff like line numbers and syntax highlighting right there in the terminal
output.
I assume David Peters was thinking along the same lines when he created
bat, a cat(1) clone with wings. In addition
to line numbers and syntax highlighting, it displays git modifications and has
paging by default. All of these are nice, and if you ever need the plain output,
it's just a bat -p away.
As with eza and z, I eventually aliased cat to bat -- otherwise I'd
often forget to use the better alternative.
find → fd
Another tool written by David Peters, fd is a find with better defaults and
syntax. No longer having to always write find -iname is a small blessing.
sed → sd
sed and awk are incredibly powerful, but also hard to learn. The simple and
by far the most common usecase for sed is a simple search-and-replace
operation, which is what sd chooses to focus on. sd supports regular
expressions rather than a messy purpose-built command language and is also
faster. sd works really well combined with fd --exec.
diff → delta
Delta offers syntax highlighting, word-level diff highlighting, an easy
side-by-side view and support for git blame. All of these are improvements
over the standard diff.
du → dust
The venerable du from GNU coreutils suffers from poor defaults. The
--human-readable flag always brings a smile to my face -- I guess that was an
afterthought?
dust has great defaults and does what you'd expect a modern du to do without
invoking any options. Now it's easy to see just how large those node_modules
and Rust's target directories are.
hexdump → hexyl
Yet another tool written by David Peters, hexyl is a better hexdump -C.
That's it for the essentials. There are probably more tools out there I ought to replace with modern alternatives, and some new ones that do not replace any existing tool like fzf -- but that's a separate blog post I might write later.
