bat is a Rust-powered drop-in replacement for cat that adds syntax highlighting for 200+ languages, a Git diff sidebar, automatic paging through less, and configurable themes. You install it with your package manager (apt install bat, brew install bat, or winget install sharkdp.bat), set alias cat='bat --paging=never' in your shell, and get a dramatically better file viewer without changing any muscle memory.
You already noticed that cat file.py in a modern terminal looks like 1983. No colors, no line numbers, no indication of which lines changed in the last commit. If the file is long, it scrolls past you in half a second. Most of us just press the up arrow and reach for less instead, then curse a little when less gives us monochrome output on our carefully themed terminal.
That friction is exactly what bat fixes. It is not a new idea — syntax-highlighting file viewers have existed for years — but bat is the one that actually stuck, because it behaves like cat does, it is fast enough to alias over cat without noticing, and its default output is pleasant enough that you never want to go back. This article goes past the "install it and enjoy" level and walks through the configuration file, the Git integration, and the half-dozen integrations that make bat the quiet hub of a modern shell setup.
Why bat beats cat: the four problems cat never solved
cat is short for concatenate. Its original 1971 purpose was to join files together and print the result to standard output — that is all. Using it to view a file was always a workaround that happened to be convenient. Everything you dislike about cat today is a symptom of asking a concatenation tool to do the job of a viewer.
bat fixes four specific things cat was never going to get right:
- Syntax highlighting.
cathas never parsed file contents, so it cannot tell you that a string literal is blue and a keyword is magenta. bat ships with over 200 syntax definitions compiled in at build time, using the same.sublime-syntaxformat as Sublime Text. - Line numbers, headers, and grid decorations.
cat -ngives you line numbers, but that is the extent of it. bat shows a header with the filename, a grid that separates header from content, and line numbers in a dimmed gutter — exactly like a code review tool. - Automatic paging. When a file is longer than your terminal,
catdumps it and you lose the top. bat detects a real TTY and hands the output tolesswith the right flags so you can scroll. Piped to another command, it falls back to plain output. - Git integration. bat reads
.gitand marks which lines are added, modified, or deleted in the left gutter — the same symbols you see in VS Code. This one alone is worth the install.
Install bat (and the batcat gotcha)
Installation is one command, with one Ubuntu/Debian quirk you need to know about.
# winget (built into Windows 11)
winget install sharkdp.bat
# or Chocolatey
choco install bat
# or Scoop
scoop install bat
On Windows, bat works in PowerShell, Windows Terminal, and WSL. For full 24-bit color, make sure you are running a terminal that supports truecolor — Windows Terminal does by default.
After installing, verify the version and look at bat --help once. The help output is the fastest way to see every flag in one place:
bat --version
# bat 0.26.1 (or newer)
bat --help | less
Daily bat: nine patterns that replace cat
Most of the time you use bat the same way you use cat — bat filename. But there are nine small variations that cover almost every real-world case. Memorize these and you will rarely reach for anything else.
# 1. View a single file with syntax highlighting and paging
bat src/app.ts
# 2. View multiple files (like `cat a b c`)
bat README.md Cargo.toml src/main.rs
# 3. Concatenate with stdin in the middle, just like cat
echo "--- header ---" | bat header.md - footer.md
# 4. Force a specific language for files with unusual extensions
bat -l json config.txt
bat -l dockerfile Containerfile
# 5. Show only a range of lines (perfect for quoting in issues)
bat --line-range=42:80 src/handlers.rs
bat --line-range=:50 README.md # first 50 lines
# 6. Plain output with no decorations — the drop-in cat replacement
bat -p src/app.ts
bat --plain --paging=never src/app.ts
# 7. Show invisible characters (tabs, trailing spaces, CRLF)
bat -A Makefile
# 8. Pipe from another command and set the language explicitly
curl -s https://api.github.com/repos/sharkdp/bat | bat -l json
# 9. Show only lines Git considers changed
bat --diff src/app.ts
The one I reach for most is --line-range. When someone on Slack asks "what does that function do around line 200?", piping bat --line-range=190:220 path/to/file.rs into the clipboard gives them a syntax-highlighted, line-numbered excerpt they can paste straight into an issue.
Configure bat once with ~/.config/bat/config
Passing flags every time gets old fast. bat reads a config file where every line is a command-line flag — anything you would type after bat on the command line. This is where the tool becomes a permanent upgrade rather than a curiosity.
First, find out where the config lives:
bat --config-file
# /home/you/.config/bat/config on Linux
# /Users/you/.config/bat/config on macOS
# C:\Users\you\AppData\Roaming\bat\config on Windows
bat --generate-config-file
# Creates the file with the default content if it does not exist
Here is a realistic starting config for a developer working across several languages:
# ~/.config/bat/config
# Use a dark theme that works well with most terminal backgrounds
--theme="OneHalfDark"
# Show line numbers and Git changes, but not the header or grid
--style="numbers,changes"
# Tab width of 2 matches modern JS/TS defaults
--tabs=2
# Map a few project-specific file names to their real syntax
--map-syntax=".ignore:Git Ignore"
--map-syntax="*.conf:INI"
--map-syntax="Dockerfile.*:Dockerfile"
# Set a custom pager invocation — less with mouse support and sensible defaults
--pager="less -FR"
Once that file exists, every bat invocation picks up those defaults. You can still override any flag on the command line — for example bat --theme=GitHub README.md for a one-off light view.
Themes
bat ships with around 25 themes. List them with file previews:
bat --list-themes
Each theme entry in the list is followed by a sample of code so you can see how it looks against your terminal background before committing. My default recommendation is OneHalfDark for dark terminals and GitHub for light ones, but this is a matter of taste. If you want to bring your own Sublime Text .tmTheme file, drop it into $(bat --config-dir)/themes/ and run bat cache --build.
Environment variables
Everything in the config file has an equivalent environment variable, which is handy for per-shell or per-project overrides:
| Variable | Equivalent to | Typical use |
|---|---|---|
BAT_THEME | --theme=... | Switch themes per terminal or per project |
BAT_STYLE | --style=... | Minimal style (plain) in scripts, full in interactive shells |
BAT_PAGER | --pager=... | Use a different pager in one specific environment |
BAT_PAGING | --paging=... | Force never when you want cat-like behaviour |
BAT_CONFIG_PATH | — | Point bat at a project-local config file |
The environment variables section of the README lists the full set.
Git integration: the diff sidebar and --diff mode
bat does not need to be told about Git. If you cd into a repo and bat some-file.rs, you will see a column of +, ~, or - markers in the left gutter next to every line that is added, modified, or deleted compared to HEAD. This works because bat uses git2 (the libgit2 Rust bindings) to read the repo state directly.
There are two ways to lean on this:
# 1. The passive case — bat automatically shows markers when --style includes "changes"
bat src/app.ts
# 2. The active case — only print lines that Git considers modified
bat --diff src/app.ts
Pairing bat --diff with xargs gives you an instant "show me everything I changed, with syntax highlighting":
git diff --name-only -z | xargs -0 bat --diff
If you want bat to drive git log and git show output too, check out delta, a dedicated Rust-powered diff viewer. We have a full delta deep dive elsewhere in this series — it's the more powerful option once you go beyond bat's --diff mode.
Integrations: man, fzf, ripgrep, less
The reason bat ends up at the centre of a modern shell setup is that almost every other CLI tool can talk to it. Here are the four integrations that pay for themselves in a week.
Syntax-highlighted man pages
Set MANPAGER to a shell command that strips the raw ANSI control sequences man emits and pipes the result through bat:
# Add to ~/.bashrc or ~/.zshrc (Debian/Ubuntu users: replace `bat` with `batcat`)
export MANPAGER="sh -c 'awk '\''{ gsub(/\x1B\[[0-9;]*m/, \"\", \$0); gsub(/.\x08/, \"\", \$0); print }'\'' | bat -p -lman'"
Now man ffmpeg, man git, or man bash come out with coloured section headers, bold options, and highlighted flags. The MANPAGER snippet is documented in the bat README and it is one of those changes where you will not go back.
fzf previews
fzf is a fuzzy finder, and it has a --preview flag that runs a shell command on each candidate line. Pointing that at bat gives you syntax-highlighted previews of every match:
# In ~/.bashrc or ~/.zshrc
export FZF_DEFAULT_OPTS="--preview 'bat --color=always --style=numbers --line-range=:500 {}'"
The --color=always flag is critical here. fzf runs the preview command with stdout attached to a pipe, not a TTY, so bat would normally disable colour. Forcing it on is what makes the preview work. The --line-range=:500 keeps bat from trying to render a 10k-line file for each preview, which would feel sluggish.
ripgrep output viewer
ripgrep itself is a separate deep dive (see grep complete guide), but bat pairs with it through the batgrep wrapper in bat-extras. batgrep foo searches for foo using ripgrep and then prints each match with bat-style highlighting plus a few lines of context. It is the closest thing to a CLI version of a "Go to definition" click.
less, but better
bat already uses less as its pager, but you can also flip the relationship — make less delegate to bat for syntax highlighting via LESSOPEN:
# ~/.bashrc
export LESSOPEN="|bat --color=always --style=numbers --paging=never %s"
export LESS="-R"
Now any tool that calls less on a file — git show, systemctl cat, journalctl, you name it — gets bat output along the way. This is advanced and occasionally fights with other tools that assume less is dumb, so enable it deliberately.
FAQ
Is bat a full replacement for cat?
For viewing files, yes. For shell scripts that need deterministic byte-for-byte output, no — keep using real cat there. The safe pattern is to alias bat over cat only in interactive shells, not in scripts. Adding alias cat='bat --paging=never' to .bashrc is fine because .bashrc is not sourced by non-interactive shells.
Why is bat showing no colors when I pipe its output?
bat auto-detects whether stdout is a TTY. When you pipe bat into another command, it assumes you want plain output and turns colors off. To force colors through a pipe, pass --color=always. This is the same convention ls and grep use.
How do I make bat the default pager for Git?
Set Git's pager to a command that pipes through bat:
git config --global core.pager "bat --plain"
For a dedicated Git diff viewer with side-by-side view, delta is the more popular choice — bat as the diff pager is a lightweight option, not the best one.
Does bat respect my terminal's color scheme?
Partially. bat picks a theme from its own bundled list — it does not read your terminal's 16-color palette the way ls --color=auto does. If you want bat to match a specific theme, pick one from bat --list-themes that complements your terminal, or use BAT_THEME_DARK and BAT_THEME_LIGHT together with COLORFGBG so bat automatically switches when you switch between light and dark terminals.
Is bat slower than cat on large files?
Measurably yes, in practice no. On a 100 MB log file, cat is obviously faster because it does zero parsing and bat is parsing every line. In normal use — source files, config files, READMEs — the difference is imperceptible. For huge files, use bat --plain --paging=never (or just cat) to skip the highlighting.
Can I add my own syntax or theme?
Yes. Drop .sublime-syntax files into $(bat --config-dir)/syntaxes/ and .tmTheme files into $(bat --config-dir)/themes/, then rebuild the cache:
bat cache --build
You can import most Sublime Text themes and syntaxes directly. The customization section of the README walks through the whole process.
Why does bat exist if there's already less with syntax highlighting?
less has no built-in syntax highlighting — you can bolt it on through LESSOPEN tricks, but that is exactly the kind of configuration bat gives you for free. bat is what cat and less would look like if they were designed today.
Wrapping Up
If you install one tool from the Modern Rust CLI Tools roundup this week, let it be bat. It takes thirty seconds to install, it is a drop-in replacement that never breaks anything, and after a day of use the cat-level output from every other tool starts to feel broken. That is the quiet sign of a good CLI upgrade.
The config file is where the real leverage is. Spend ten minutes on ~/.config/bat/config, pick a theme, set the style, map your unusual file names to the right syntax, and then forget about it. The MANPAGER and fzf integrations are bonus wins that cost two lines of shell each and pay for themselves every day you work in a terminal.
For the engineering story behind why Rust tools like bat, ripgrep, and fd outrun their classic counterparts, read Why Rust CLI Tools Are So Fast. For the map of which classic tool each modern replacement covers, see the CLI tools map. And if you are rebuilding your shell setup from scratch, the dotfiles and environment guide is where to put all of the config snippets in this article so they follow you between machines.