Skip to main content

bat — A Better cat With Syntax Highlighting

Overview

bat is a cat-style file viewer with a nicer default UX:

  • Syntax highlighting (auto-detect by filename / content)
  • Line numbers, headers, and optional grid styling
  • Pager integration (so large files don’t flood your terminal)
  • Git integration (show changes inline in tracked files)

It’s ideal for reading WordPress configs, Nginx/Apache configs, shell scripts, and logs on a VPS—without losing your place.

Tool Snapshot
  • Core Function: Pretty-print files (or stdin) to stdout with optional paging.
  • Primary Benefit: Faster comprehension of code/configs than plain cat.
  • Where to Use: Reviewing scripts, debugging configs, inspecting logs, teaching snippets.
  • Workflow: bat [OPTIONS] [FILE...] (or read from stdin).

Prerequisites

  • Linux (Debian/Ubuntu, Fedora, Arch, etc.) or macOS
  • Terminal access
  • Some text/code files to view

System Check

On Debian/Ubuntu, the binary is often named batcat.

which bat || which batcat

Expected output:

/usr/bin/bat
# or on Debian/Ubuntu:
# /usr/bin/batcat

If your system uses batcat, you may want an alias for convenience:

alias bat=batcat

Install (common distros)

# Debian/Ubuntu
sudo apt install bat

# Fedora
sudo dnf install bat

# Arch
sudo pacman -S bat

# macOS (Homebrew)
brew install bat
bat in scripts

By default, bat may page output and use color. In scripts and pipelines, prefer:

  • -p (plain, no decorations)
  • --paging=never
  • --color=never (if the next tool can’t handle ANSI colors)

Core Syntax

bat [OPTIONS] [FILE...]
  • FILE...: one or more files. If omitted, bat reads from stdin.
  • OPTIONS: control style (numbers/header/changes), theme, paging, color, ranges, and language.

Defaults you should know

  • Syntax highlighting: auto-detected based on filename/content
  • Paging: uses a pager when appropriate (prevents terminal flooding)
  • Header: shows filename and a ruler by default

Options & Flags

OptionWhat it doesWhen you’d use itExample
:::-:--
-n, --numberShow line numbersDebugging / code reviewbat -n app.php
--style=...Control UI (header/grid/numbers/changes)Reduce clutter or add contextbat --style=plain file
--theme=...Pick a themeImprove readabilitybat --theme=TwoDark file.py
-A, --show-allShow non-printing charsWhitespace/CRLF debuggingbat -A Makefile
-p, --plainPlain output (no header/pager)Script-friendly outputbat -p config.yml
--paging=never/auto/alwaysControl paging behaviorDisable paging in pipelinesbat --paging=never file
-l, --language=...Force language syntaxUnknown extensions / stdinbat -l php file
-r, --line-range=...Show specific line range(s)Quick previews/snippetsbat -r 10:40 file
--highlight-line=...Highlight linesTeaching/review emphasisbat --highlight-line 42 file
--color=always/auto/neverControl ANSI colorsPiping into other toolsbat --color=never file
--diffShow Git diff style for fileReview modified linesbat --diff file
--map-syntax ...Map extension to languageCustom templates/typesbat --map-syntax .tpl:php
Quick “clean output” presets
  • Interactive, readable: bat -n --style=numbers,changes,header file
  • Minimal and script-safe: bat -p --paging=never --color=never file

Common Patterns

bat /var/www/html/wp-config.php
bat -n /etc/nginx/sites-available/default
bat --theme=TwoDark --style=numbers,header /etc/hosts

Best Practices

  • Use bat interactively for readability; switch to -p and --paging=never in automation.

  • If output is “too busy,” simplify style:

    bat --style=plain file
  • When debugging whitespace/indentation (Makefile/YAML), use:

    bat -A file
  • For files without extensions or stdin, force language:

    bat -l bash script
  • If you want bat as a cat replacement, do it only for interactive shells:

    • Good idea: alias cat=bat in your shell profile
    • Bad idea: relying on that alias inside scripts
bat vs cat vs nano — what to use and when?
  • cat: Fast, minimal, ubiquitous. Great for quick dumps, combining files, and pipelines. No highlighting.
  • bat: Like cat, but optimized for humans—highlighting, numbers, paging, Git changes.
  • nano: Editor. Use it when you need to modify content, not just view it.

Rule of thumb

  • Viewing code/configs/logs: bat
  • Simple piping/compatibility: cat
  • Editing: nano

Troubleshooting

ProblemLikely CauseFix
:--:-:-
No colorsOutput is piped / non-TTYUse --color=always (interactive) or avoid piping
Pager appears when you don’t want itDefault paging behaviorAdd --paging=never or use -p
No syntax highlightingUnknown extensionUse -l <lang> or --map-syntax
Output feels too “busy”Header/grid/numbers/changes enabledUse --style=plain or --style=numbers
bat not found on Debian/UbuntuBinary is batcatUse batcat or alias bat=batcat
Color in pipelines

If you pipe bat output into tools that don’t expect ANSI codes (or into files), use --color=never to avoid escape sequences in saved output.

Cheat Sheet

# Basics
bat file.txt # Highlighted file with header and pager
bat -n file.php # Show line numbers
bat -A Makefile # Show all characters (tabs, EOLs)

# Styles, themes, and paging
bat --style=numbers,changes file.js # Custom style
bat --theme=TwoDark file.py # Use specific theme
bat --paging=never file.log # Disable paging
bat -p config.yml # Plain mode (no header, no pager)

# Ranges and highlighting
bat -r 10:40 app.php # Only lines 10–40
bat --highlight-line 42 app.php # Highlight line 42
bat -r :50 --highlight-line 20,25 app.php # First 50 lines, highlight some

# Language and stdin
bat -l json settings # Force JSON highlighting
cat data.json | bat -l json # Pretty-print JSON from stdin

# Git changes
bat --diff src/main.py # Show changed lines with diff style

Mini Quiz

  1. Which option controls the presence of headers, numbers, and other UI elements?
  2. How do you disable the pager for scripts/pipelines?
  3. How do you show non-printing characters like tabs and end-of-line markers?
  4. What do you use when bat guesses the wrong language?
  5. How do you show only lines 100–150 of a file?
Show answers
  1. --style=...
  2. --paging=never (and/or -p)
  3. -A / --show-all
  4. -l <language> (or --map-syntax ...)
  5. -r 100:150 file

Worked Examples

About expected output

Output below is shown without colors. In a real terminal, syntax highlighting and UI decorations will be colored depending on theme and terminal capabilities.

Example files used in demos

Assume we have app.php:

<?php
echo "Hello, world!\n";
$version = "1.0.0";
if ($version === "1.0.0") {
echo "Stable\n";
}

And a script file named script (no extension):

#!/bin/bash
echo "Backup started"

1) Basic view of a file

bat app.php

Expected output (simplified):

───────┬────────────────────────────────────────────
│ File: app.php
───────┼────────────────────────────────────────────
1 │ <?php
2 │ echo "Hello, world!\n";
3 │ $version = "1.0.0";
4 │ if ($version === "1.0.0") {
5 │ echo "Stable\n";
6 │ }
───────┴────────────────────────────────────────────

2) Show line numbers explicitly

bat -n app.php

Expected output:

───────┬────────────────────────────────────────────
│ File: app.php
───────┼────────────────────────────────────────────
1 │ <?php
2 │ echo "Hello, world!\n";
3 │ $version = "1.0.0";
4 │ if ($version === "1.0.0") {
5 │ echo "Stable\n";
6 │ }
───────┴────────────────────────────────────────────

3) Plain mode (no header, no pager, no decorations)

bat -p app.php

Expected output:

<?php
echo "Hello, world!\n";
$version = "1.0.0";
if ($version === "1.0.0") {
echo "Stable\n";
}

4) Show all characters (tabs/EOL markers)

bat -A app.php

Expected output (illustrative):

───────┬────────────────────────────────────────────
│ File: app.php
───────┼────────────────────────────────────────────
1 │ <?php␊
2 │ echo·"Hello,·world!\n";␊
3 │ $version·=·"1.0.0";␊
4 │ if·($version·===·"1.0.0")·{␊
5 │ ······echo·"Stable\n";␊
6 │ }␊
───────┴────────────────────────────────────────────

5) Show only a line range (lines 2–4)

bat -r 2:4 app.php

Expected output:

───────┬────────────────────────────────────────────
│ File: app.php
───────┼────────────────────────────────────────────
2 │ echo "Hello, world!\n";
3 │ $version = "1.0.0";
4 │ if ($version === "1.0.0") {
───────┴────────────────────────────────────────────

6) Highlight a specific line

bat --highlight-line 3 app.php

Expected output (line highlighted; shown here with >>):

───────┬────────────────────────────────────────────
│ File: app.php
───────┼────────────────────────────────────────────
1 │ <?php
2 │ echo "Hello, world!\n";
>> 3 │ $version = "1.0.0";
4 │ if ($version === "1.0.0") {
5 │ echo "Stable\n";
6 │ }
───────┴────────────────────────────────────────────

7) Use a specific theme

bat --theme=TwoDark app.php

Expected output: (same structure; colors differ based on theme)

───────┬────────────────────────────────────────────
│ File: app.php (theme: TwoDark)
───────┼────────────────────────────────────────────
1 │ <?php
2 │ echo "Hello, world!\n";
3 │ $version = "1.0.0";
4 │ if ($version === "1.0.0") {
5 │ echo "Stable\n";
6 │ }
───────┴────────────────────────────────────────────

8) Force language for a file without extension

bat -l bash script

Expected output:

───────┬────────────────────────────────────────────
│ File: script (language: Bash)
───────┼────────────────────────────────────────────
1 │ #!/bin/bash
2 │ echo "Backup started"
───────┴────────────────────────────────────────────

9) View configuration without pager (pipeline/script safe)

bat --paging=never -p /etc/hosts

Expected output (simplified):

127.0.0.1 localhost
127.0.1.1 my-vps
# IPv6 entries...
::1 localhost ip6-localhost ip6-loopback

10) Pretty-print JSON from stdin

cat data.json | bat -l json

Expected output (simplified):

───────┬────────────────────────────────────────────
│ STDIN (language: JSON)
───────┼────────────────────────────────────────────
1 │ {
2 │ "name": "site",
3 │ "enabled": true
4 │ }
───────┴────────────────────────────────────────────

11) Show only the top of a large log file (first 50 lines)

bat -r :50 /var/log/nginx/access.log

Expected output: (first 50 lines shown; truncated here)

───────┬────────────────────────────────────────────
│ File: /var/log/nginx/access.log
───────┼────────────────────────────────────────────
1 │ 192.168.0.10 - - [12/Nov/2025:10:00:01 +0000] "GET / HTTP/1.1" 200 512 "-" "curl/7.81.0"
2 │ 192.168.0.11 - - [12/Nov/2025:10:00:02 +0000] "GET /favicon.ico HTTP/1.1" 404 162 "-" "curl/7.81.0"
3 │ ...
50 │ ... (line 50)
───────┴────────────────────────────────────────────

12) Show Git changes inline

bat --diff src/main.py

Expected output (illustrative markers):

───────┬────────────────────────────────────────────
│ File: src/main.py
───────┼────────────────────────────────────────────
1 │ import sys
- 2 │ old_line = "this used to be here"
+ 2 │ new_line = "this line changed"
3 │ print(new_line)
───────┴────────────────────────────────────────────

13) Customize style to show only numbers and code

bat --style=numbers app.php

Expected output:

1 <?php
2 echo "Hello, world!\n";
3 $version = "1.0.0";
4 if ($version === "1.0.0") {
5 echo "Stable\n";
6 }

14) Use bat in a pipeline (disable paging)

ps aux | bat --paging=never

Expected output (first lines):

───────┬────────────────────────────────────────────
│ STDIN
───────┼────────────────────────────────────────────
1 │ USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
2 │ root 1 0.0 0.1 169340 9300 ? Ss 10:00 0:02 /sbin/init
3 │ www-data 1234 0.1 1.2 523456 48200 ? S 10:01 0:10 php-fpm: pool www
4 │ ...
───────┴────────────────────────────────────────────

15) View a unified diff with syntax highlighting

diff -u old.conf new.conf | bat -l diff

Expected output:

───────┬────────────────────────────────────────────
│ STDIN (language: diff)
───────┼────────────────────────────────────────────
1 │ old.conf
2 │ +++ new.conf
3 │ @@ -1,3 +1,3 @@
4 │ -max_clients = 50
5 │ +max_clients = 100
6 │ timeout = 30
───────┴────────────────────────────────────────────