Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

354 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🦇 batless

A fast, non-blocking cat/bat alternative

No pager, no highlighting, no waiting — just fast, predictable file viewing for scripts, pipelines, and terminals alike

Crates.io Crates.io Downloads GitHub Downloads License: MIT GitHub Release

CI Security CodeQL Fuzz Testing Health Check Codecov OpenSSF Scorecard

Rust Binary Size

🎯 Why batless?

bat is great, but it uses a pager and syntax highlighting — both of which are wrong for scripts, CI, and automated pipelines. cat never blocks, but it's plain-text only with no structure. batless sits between them: it never blocks, never pages, and adds exactly two things cat doesn't have — a --mode=json output for scripting and a --mode=index symbol table for a quick structural map of a file, without loading or parsing the whole thing yourself.

# Drop-in cat replacement, never blocks
batless large-file.log

# cat -n / cat -b compatible line numbering
batless -n --plain file.py

# Structured JSON for scripts
batless --mode=json file.py | jq '.total_lines'

# Symbol table — functions, structs, classes, with line ranges
batless --mode=index src/main.rs | jq '.symbols[] | "\(.line_start): \(.kind) \(.name)"'

Core guarantee: batless will NEVER wait for user input or block your pipeline.

🚀 Quick Start

Installation

Option A: Pre-built Binaries (Fastest)

# Linux (x86_64)
curl -L https://github.com/docdyhr/batless/releases/latest/download/batless-x86_64-unknown-linux-gnu.tar.gz | tar xz

# macOS (Intel)
curl -L https://github.com/docdyhr/batless/releases/latest/download/batless-x86_64-apple-darwin.tar.gz | tar xz

# macOS (Apple Silicon)
curl -L https://github.com/docdyhr/batless/releases/latest/download/batless-aarch64-apple-darwin.tar.gz | tar xz

Option B: Via Cargo

cargo install batless

Option C: Homebrew (macOS/Linux)

brew tap docdyhr/batless
brew install batless

Basic Usage

# Plain text (default mode — no pager, no highlighting)
batless file.py

# Structured JSON output
batless --mode=json file.py

# Symbol index — functions, structs, classes with line ranges
batless --mode=index src/main.rs

# Multi-file symbol index — walk a directory, one NDJSON line per file
batless --mode=index src/ | jq -c 'select(.symbol_count > 0) | {file, symbol_count}'

# Line numbers (cat -n / cat -b compatible — requires --plain or --mode=plain)
batless -n --plain file.py
batless -b --plain file.py

# Limit output
batless --max-lines=50 large-file.py
batless --max-bytes=10000 huge-file.log

# Get version info as JSON
batless --version-json

🌟 What Makes batless Different

🏆 Feature Comparison

Feature batless bat cat
Never Blocks ✅ Guaranteed ❌ Uses pager
Symbol Index (--mode=index)
JSON Output ✅ First-class
cat -n/cat -b compatible
Predictable in scripts/CI ✅ Same output every time ❌ Pager varies by terminal
Syntax Highlighting ❌ Use bat ✅ Rich
Interactive Human Use ❌ Not the goal

🚀 Core Capabilities

Non-Blocking Guarantees

  • 🚫 NEVER uses a pager — no less, no more, no blocking
  • NEVER waits for input — always streams output immediately
  • 🔄 NEVER hangs in pipes — safe for |, >, and subprocess calls
  • 📊 ALWAYS returns quickly — bounded reads via --max-lines/--max-bytes

Language Support

  • 🔍 Language auto-detection with manual override (--language)
  • 🌐 Universal plain output — works with any text-based file format
  • 🗂️ Symbol extraction for Rust, Python, JavaScript, TypeScript, and a broad set of others via a lightweight heuristic extractor (functions, classes, structs, imports)

Output Modes

  • 📊 Three output modes: plain (default), json, index
  • 📏 Smart limiting by lines (--max-lines) and/or bytes (--max-bytes)
  • 🎯 Predictable behavior — identical output in terminal or pipe
  • ✂️ Content stripping--strip-comments/--strip-blank-lines for a denser view of a file
  • 📦 Single <2MB binary with minimal dependencies (no bundled parser toolchains)

🚫 What batless is NOT

batless has a focused design philosophy. It intentionally does NOT provide:

Features We Don't Implement (By Design)

Feature Why Not? Use Instead
Pattern Search That's grep's job grep -rn "pattern" path/
Arbitrary Line Ranges Beyond our scope sed -n '10,50p' file
File Globbing Shell handles this batless *.py (shell expands)
Interactive Paging We're non-blocking Use bat or less
Syntax Highlighting Adds weight for no automation benefit Use bat
Git Integration Keep it simple Use git diff or bat
File Management Not a file browser ls, find, fd
Text Editing Viewer only Use your editor

Common Misconceptions

"batless is a drop-in replacement for bat"Reality: batless is purpose-built for scripts, CI, and automation — for interactive human reading, bat is the better tool.

"batless should add grep-like search"Reality: Unix philosophy — do one thing well. Use grep for searching.

"batless needs more output modes"Reality: Less is more. plain, json, and index cover what people actually use — usage data backs this up (see below).

When NOT to Use batless

  • 👤 Interactive code review: Use bat — it has syntax highlighting and paging built for human reading
  • 🔍 Searching code: Use grep, rg (ripgrep), or ag (silver searcher)
  • 📝 Editing files: Use your favorite editor
  • 📊 Complex analysis: Use language-specific tools (pylint, rust-analyzer, etc.)

Our Philosophy

Do ONE thing well: view files without ever blocking. For everything else —
highlighting, searching, editing, interactive paging — there's already a
better tool. Add features only when real usage justifies the weight.

batless's own scope decisions are usage-driven, not guesswork: --mode=plain accounts for 84% of real invocations, --mode=index for another 10% — everything else in the CLI is either free (line limits, color control) or has demonstrated real use. See docs/PHILOSOPHY_AND_SCOPE.md for the full reasoning.

📖 Usage Examples

Basic File Viewing

# Plain text (default — no pager, no highlighting, no colors unless piped to a terminal)
batless main.rs

# Force no color even in a terminal
batless --color=never main.rs

# With line numbers
batless -n --plain main.rs

# Limit output
batless --max-lines=50 large-file.py
batless --max-bytes=10000 huge-file.log

JSON & Scripting Workflows

# JSON output for downstream processing
batless --mode=json src/main.rs | jq '.total_lines'

# Pretty-printed JSON
batless --mode=json --json-pretty src/main.rs

# JSON lines as {"n": N, "text": "..."} objects instead of plain strings
batless --mode=json --with-line-numbers src/main.rs

# CI/CD context: capture a bounded slice of a failing test file
batless --mode=json --max-lines=100 failing-test.rs > context.json

# Machine-readable version metadata
batless --version-json

Symbol Index

# Symbol table for one file
batless --mode=index src/main.rs

# Walk a directory — one compact NDJSON line per file
batless --mode=index src/ | jq -c 'select(.symbol_count > 0) | {file, symbol_count}'

# Find every public function across a Rust crate
find src -name "*.rs" -exec batless --mode=index {} \; \
  | jq -c '.symbols[] | select(.kind=="function" and .visibility=="pub")'

Pipeline Integration

# Use as PAGER replacement
PAGER="batless --plain" gh pr view 42

# Process multiple files
find src -name "*.rs" -exec batless --mode=index {} \;

# Combine with grep
grep -l "TODO" src/*.py | xargs batless -n --plain

# Stream stdin
cat file.rs | batless --language=rust

🎨 Configuration

Language Detection

# Auto-detect (default)
batless file.txt

# Force specific language
batless --language=python unknown.file

# List supported languages
batless --list-languages

Content Stripping

# Strip comment-only lines
batless --strip-comments src/main.rs

# Strip blank lines
batless --strip-blank-lines src/main.rs

# Combine both — JSON output includes a compression_ratio field
batless --mode=json --strip-comments --strip-blank-lines src/main.rs | jq '.compression_ratio'

Shell Completions

batless includes built-in shell completion support for bash, zsh, fish, and PowerShell.

Bash

# Generate and install completions
batless --generate-completions bash > ~/.local/share/bash-completion/completions/batless

# Or for system-wide installation
sudo batless --generate-completions bash > /usr/share/bash-completion/completions/batless

# Then reload your shell or source the completion file
source ~/.local/share/bash-completion/completions/batless

Zsh

# Generate and install completions
batless --generate-completions zsh > ~/.zsh/completions/_batless

# Add to your ~/.zshrc (if not already present)
fpath=(~/.zsh/completions $fpath)
autoload -Uz compinit && compinit

# Then reload your shell
exec zsh

Fish

# Generate and install completions
batless --generate-completions fish > ~/.config/fish/completions/batless.fish

# Completions are automatically loaded in new fish shells

PowerShell

# Generate and add to your profile
batless --generate-completions power-shell | Out-String | Invoke-Expression

# Or save to your profile for persistence
batless --generate-completions power-shell >> $PROFILE

🔧 CLI Options

Output Modes

  • --mode <MODE> — Output mode: plain (default), json, index
  • --plain — Plain text output (equivalent to --mode=plain, also used for PAGER compatibility)
  • --mode=json — Structured JSON output
  • --mode=index — Machine-readable symbol table (kind, name, line range, visibility); pass a directory to walk it and emit one NDJSON line per file

Limiting Output

  • --max-lines <N> — Limit output to N lines
  • --max-bytes <N> — Limit output to N bytes

Display Options

  • -n, --number — Show line numbers (cat -n compatibility; requires --plain/--mode=plain)
  • -b, --number-nonblank — Number non-blank lines only (cat -b compatibility; requires --plain/--mode=plain)
  • --language <LANG> — Force specific language detection
  • --color <MODE> — Color control: auto (default), always, never
  • --strip-ansi — Strip ANSI escape codes from output

JSON Output Options

  • --json-pretty — Pretty-print JSON output
  • --with-line-numbers — JSON lines array uses {"n": N, "text": "..."} objects instead of plain strings
  • --strip-comments — Strip comment-only lines from output (adds compression_ratio to JSON output)
  • --strip-blank-lines — Strip blank lines from output (adds compression_ratio to JSON output)

JSON Output Fields

When using --mode=json, the output includes:

Field Type Description
file string File path
language string|null Detected language
lines array File lines (strings, or {"n","text"} objects with --with-line-numbers)
mode string "json"
processed_lines integer Number of lines actually in lines
total_lines integer Line count in original file
total_lines_exact boolean Whether total_lines covers the full file
total_bytes integer File size in bytes
truncated boolean Whether output was truncated
truncated_by_lines boolean Whether truncation was due to --max-lines
truncated_by_bytes boolean Whether truncation was due to --max-bytes
encoding string Detected encoding
syntax_errors array Encoding/processing errors encountered, if any
compression_ratio number|null original/stripped line ratio (present only with --strip-comments/--strip-blank-lines)

When using --mode=index, the output includes:

Field Type Description
file string File path
language string|null Detected language
mode string "index"
symbol_count integer Number of symbols found
symbols array Symbol table entries
symbols[].kind string function, struct, class, impl, trait, import, etc.
symbols[].name string Symbol identifier name
symbols[].line_start integer 1-based start line
symbols[].line_end integer (key omitted if unset) 1-based end line; the field exists in the schema but the current regex/heuristic extractor never populates it, so the key is always omitted from the JSON object today, not present as null
symbols[].signature string Declaration line, trimmed
symbols[].visibility string|null pub, private, export, local, depending on language
total_lines integer Line count
total_bytes integer File size in bytes

Utility

  • --list-languages — Show all supported languages
  • --config <PATH> — Configuration file path (defaults to auto-discovery of .batlessrc/batless.toml)
  • --debug — Enable debug mode with detailed processing information
  • --generate-completions <SHELL> — Generate shell completions (bash, zsh, fish, power-shell)
  • --version — Show version information
  • --version-json — Machine-readable version metadata
  • --help — Show detailed help information

🏗️ Architecture

batless is built with:

  • Rust — memory safety and performance
  • A small, focused dependency set — clap for argument parsing, serde/serde_json for JSON, encoding_rs for encoding detection; no bundled parser toolchains
  • Bounded reads--max-lines/--max-bytes cap memory use on large files
  • Modular design — clean separation between config parsing, file processing, and output formatting

See docs/ARCHITECTURE.md for technical details.

🤝 Contributing

We welcome contributions! Please see:

Development Setup

# Clone repository
git clone https://github.com/docdyhr/batless.git
cd batless

# Build
cargo build

# Run tests
cargo test

# Run with example
cargo run -- src/main.rs

📊 Performance

  • Startup time: <5ms typical on modern hardware
  • Binary size: <2MB stripped (1.5MB measured on macOS arm64, down from 8.0MB before the v0.7.0 scope reduction)
  • Memory usage: Bounded by --max-lines/--max-bytes
  • Throughput: Limited only by disk I/O

Note: Performance varies by hardware. Benchmarks on typical developer workstation.

📜 License

MIT License - see LICENSE for details.

🔗 Links

🙏 Acknowledgments

  • Inspired by bat by @sharkdp
  • Community feedback and contributions

Built for scripts, CI, and pipelines that can't afford to block

⭐ Star on GitHub | 📦 Install Now | 📖 Read the Docs

About

Minimal, blazing-fast syntax viewer for the terminal — never blocks, never pages, never hangs. Perfect for code, logs, and AI pipelines.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages