I resisted bat for an embarrassingly long time. It sits at the top of every “modern CLI tools” listicle, and my reflex when a tool is that hyped is to assume it is a solution in search of a problem. cat works. cat has worked since 1971. What could possibly justify replacing a command whose entire job is to dump a file to the screen? So I installed bat expecting to shrug and move on. Instead it quietly became one of the handful of tools I would genuinely miss if you took it away. Here is what changed my mind.
What bat actually is
bat, written by David Peter (sharkdp, the same person behind fd and hyperfine), is a cat clone with, as the tagline puts it, wings. At the core it does exactly what cat does: print the contents of a file to your terminal. The difference is everything wrapped around that core. bat gives you syntax highlighting for a few hundred languages, line numbers, a git change gutter, automatic paging for long files, and non-printable-character display, all on by default and all tuned to be genuinely useful rather than decorative.
The design decision that makes it trustworthy is context awareness. When bat’s output is going to a real terminal, you get the full colored, decorated view. The instant it detects that output is being piped into another command or redirected to a file, it strips all of that and behaves as a plain cat, printing raw bytes. That means bat file.json | jq and bat file.txt > copy.txt do exactly what the cat versions do, with no surprises. It is the detail that lets you alias bat over cat without breaking anything.
Installing it
Installation is a one-liner on most systems. On macOS it is brew install bat. On Arch it is pacman -S bat. With a Rust toolchain, cargo install bat builds it anywhere. Prebuilt binaries live on the GitHub releases page for everything else.
Debian and Ubuntu users hit one wrinkle worth knowing up front. The apt install bat package installs the binary as batcat, not bat, because Debian already ships a different program at /usr/bin/bat. Everything works identically, you just call batcat, or drop alias bat=batcat into your shell rc to get the expected name back. It trips up newcomers constantly, so if bat: command not found greets you after an apt install, that is why.
The features that actually earn their keep
Plenty of tools pile on features nobody uses. bat is refreshingly the opposite: almost everything it adds is something I reach for constantly.
Syntax highlighting is the headline, and it is better than it needs to be. bat bundles a large collection of syntax definitions, so it colors not just the obvious languages but config files, Dockerfiles, TOML, YAML, SQL, and dozens more. Reading a dense JSON config or a nested YAML file with real highlighting instead of a wall of monochrome text genuinely reduces the effort of parsing it in your head. You can also pick from a stack of themes with bat --list-themes and set a favorite, which matters if your terminal background fights the default.
The git integration surprised me the most. When you bat a file that lives in a git repo, the left gutter marks which lines are added, modified, or removed relative to the index. It turns a quick bat somefile.py into a lightweight “what did I change here” view without opening an editor or running a diff. For a fast sanity check before committing, it is lovely.
Automatic paging means bat hands long output to a pager (less, by default) so you are never blasted with three thousand lines scrolling off the top of your screen. For short files it prints inline like cat; for long ones it pages. You stop having to decide up front whether to use cat or less, because bat picks correctly for you.
Line numbers and non-printable display round it out. Line numbers show by default, which is constantly handy when someone says “look at line 214.” And bat -A reveals tabs, spaces, and newlines explicitly, which is the fastest way I know to debug a file that “looks fine” but has mixed whitespace or Windows line endings hiding in it.
Where bat fits into a real workflow
The single highest-value move is wiring bat into other tools as a previewer. Paired with a fuzzy finder like fzf, a preview command of bat --color=always {} turns file selection into a live, syntax-highlighted preview pane, so you see what a file contains before you open it. That combination alone changed how I navigate unfamiliar repos.
bat also makes a great pager for other commands. You can set it as the pager for --help output or use it to colorize man pages by pointing the man pager at bat with the right styling flags. Reading a man page with syntax-highlighted examples and section headers is a small quality-of-life bump that adds up over a career of squinting at monochrome docs.
For everyday reading, I alias cat to bat on my personal machines and it has been frictionless. Because bat falls back to plain output the moment it is piped or redirected, nothing in my scripts broke. The interactive cat file.py just got better, and the scripted cat file | grep kept behaving exactly as before. That is the ideal shape for a replacement tool: strictly better where a human is looking, invisible everywhere else.
One habit worth building: use bat -p (plain) or bat -pp when you want the content with no decorations for a quick copy-paste, and lean on bat -r 50:80 file to print just a line range. Those two flags cover the cases where the full decoration is more than you want.
The honest trade-offs
bat is not free of cost, and pretending otherwise would be dishonest. Syntax highlighting takes CPU, so on a genuinely huge file, a multi-megabyte log or a minified bundle, bat is slower than plain cat. In day-to-day use you never notice because it pages the output and you are only ever looking at the first screen, but if you are dumping enormous files in a tight loop, plain cat still wins. I keep cat around under its real name for exactly that.
It is also one more binary that is not guaranteed to exist. cat is on every Unix machine that has ever booted; bat is not. For anything shared, a script other people run, a CI job, a Dockerfile someone else maintains, I use cat, because assuming bat exists is a portability bug waiting to happen. bat is a tool for your interactive shell, not a dependency to bake into infrastructure.
And a minor gripe: the first time you use it, the defaults can feel busy. If the grid lines and headers are too much, bat --style=numbers or bat --style=plain dials it back, and you can make that permanent in the config file. Once tuned, it disappears into the background the way a good tool should.
Theming and config, briefly
If you are going to live in bat, spend five minutes on the config file. Running bat --generate-config-file drops a commented template in the right place, and from there you can set your default theme, style components, and pager once so every invocation behaves the way you want.
Theme choice matters more than it sounds. bat ships with a couple dozen themes, and the default may clash with your terminal background, washing out the highlighting into something worse than plain text. Run bat --list-themes to preview them against a sample file, then pin your favorite in the config. On a dark terminal I like the higher-contrast themes; on a light one the defaults that look muddy suddenly pop. It is a two-minute investment that decides whether the headline feature actually helps you.
The --style option is the other big lever. It controls which decorations show: line numbers, the git gutter, the file header, and grid lines. If the full treatment feels noisy, --style=numbers,changes keeps the two most useful pieces and drops the rest. Set it in config and you never think about it again. Between a good theme and a trimmed style, bat goes from “impressive demo” to a tool you stop noticing because it just fits.
The Bottom Line
bat is that rare “modern replacement” that actually deserves the hype. It does everything cat does, stays perfectly compatible in pipes and scripts, and adds syntax highlighting, git awareness, paging, and line numbers that you will use every single day. The cost is minor: a little slowness on giant files and one more binary that is not universal. Neither of those matters for interactive use, which is exactly where bat lives.
My advice is simple. Install it, use it as an fzf previewer, and alias cat to bat on your own machines while keeping plain cat for scripts and huge files. Within a week you will forget it was ever a decision. It is not a flashy tool, and that is the point: it takes the most boring command in your terminal and quietly makes it better, then gets out of the way. That is exactly what I want from software, and it is why bat earned a permanent spot in my dotfiles.