# How Bat Handles Input from Files and Standard Input (stdin)

> Discover how bat handles file and standard input unified through a single Input abstraction. Learn about buffered readers, encoding detection, and the highlighting pipeline.

- Repository: [David Peter/bat](https://github.com/sharkdp/bat)
- Tags: internals
- Published: 2026-03-06

---

**Bat unifies all text sources through a single `Input` abstraction that wraps both files and stdin into a buffered `InputReader`, performs IO-circle safety checks via the `clircle` crate, and automatically detects UTF-16 encoding before feeding the syntax-highlighting pipeline.**

The `bat` command-line tool by sharkdp/bat serves as a syntax-highlighting clone of `cat` that must seamlessly process both regular files and piped standard input. Whether you run `bat file.txt` or `echo "hello" | bat`, the program handles input from files and standard input through a unified architecture defined in [`src/input.rs`](https://github.com/sharkdp/bat/blob/main/src/input.rs) and [`src/bin/bat/input.rs`](https://github.com/sharkdp/bat/blob/main/src/bin/bat/input.rs). This design ensures consistent behavior across encoding detection, paging support, and circular IO prevention.

## The Input Abstraction Architecture

Bat abstracts every source of text through the **`Input`** type, an enum that normalizes access patterns regardless of origin. According to the bat source code in [`src/input.rs`](https://github.com/sharkdp/bat/blob/main/src/input.rs), the `InputKind` variant distinguishes between three sources:

- **`OrdinaryFile`** – A path on the filesystem requiring open permissions and metadata checks.
- **`StdIn`** – The standard input stream, typically used when data is piped from another command.
- **`CustomReader`** – An internal variant reserved for programmatic use in tests and library consumers.

This unified abstraction allows downstream components, particularly the pretty printer in [`src/pretty_printer.rs`](https://github.com/sharkdp/bat/blob/main/src/pretty_printer.rs), to consume data without knowing whether it originated from a file descriptor or a pipe.

## Creating Input Sources

Before opening any reader, bat constructs the appropriate `Input` variant through convenience helpers defined in [`src/bin/bat/input.rs`](https://github.com/sharkdp/bat/blob/main/src/bin/bat/input.rs):

- **`new_file_input`** – Builds an `Input` for an ordinary file path and optionally attaches a user-visible display name.
- **`new_stdin_input`** – Constructs an `Input` representing `STDIN`, often paired with the `named` helper to set a virtual filename for the header line.

When you invoke `bat --file-name=example.txt` while piping data, the program calls `new_stdin_input(Some("example.txt"))` to attach metadata that appears in the output header, ensuring the pretty printer treats the stream as if it were a named file.

## Opening Readers and Safety Validation

The `Input::open` method in [`src/input.rs`](https://github.com/sharkdp/bat/blob/main/src/input.rs) (lines 90–150) transforms the abstract `Input` into a concrete `OpenedInput` containing an `InputReader`. This phase implements critical safety and performance logic:

1. **IO-Circle Detection** – Before opening a file, bat verifies that the input path is not identical to the program's output destination using the `clircle` crate. This prevents infinite loops when users execute commands like `bat file.txt | bat > file.txt`.
2. **Buffered Reading** – For `OrdinaryFile` inputs, the method opens the file, validates it is not a directory, and wraps it in a `BufReader`. For `StdIn` inputs, it receives a `BufRead` object representing the live stream.
3. **Unbuffered Mode** – When paging is active (via `less`), bat enables an `unbuffered` flag that switches to non-blocking reads, allowing the pager to receive partial data without waiting for complete lines.

## Content Type Detection and Line Reading

Once opened, the `InputReader::new` constructor (lines 60–78) performs encoding detection by reading the first line into a cache and running the **content-inspector** crate. This inspection determines whether the data is UTF-8, UTF-16LE, or UTF-16BE, enabling bat to handle UTF-16 files as text without requiring explicit user flags.

The `InputReader::read_line` method manages subsequent consumption:

- First call returns the cached `first_line` used for encoding detection.
- Subsequent calls read from the inner buffered reader, automatically converting UTF-16 byte sequences when needed.
- In unbuffered mode, the method returns partial data immediately to support interactive paging.

## Integration with the Pretty Printer

After initialization, the `OpenedInput` containing the configured `InputReader` is passed to the pretty printer in [`src/pretty_printer.rs`](https://github.com/sharkdp/bat/blob/main/src/pretty_printer.rs). The printer iterates over lines using the reader's buffered interface, applies syntax highlighting based on the input's associated name, and manages pagination. This pipeline operates identically whether the source is a 10-gigabyte log file or a stdin stream from a remote pipe.

### Practical Usage Examples

```bash

# Read a regular file with syntax highlighting

bat src/input.rs

# Process piped stdin (automatically detected)

echo "Hello, world!" | bat

# Attach a custom filename to stdin for header display

printf "data\n" | bat --file-name=example.txt

```

## Summary

- **Unified Abstraction** – The `Input` type in [`src/input.rs`](https://github.com/sharkdp/bat/blob/main/src/input.rs) treats files, stdin, and custom readers identically through the `InputKind` enum.
- **Safety First** – IO-circle detection using the `clircle` crate prevents data corruption when input and output target the same file.
- **Encoding Intelligence** – Automatic UTF-8/UTF-16 detection via `content_inspector` handles international text without manual configuration.
- **Flexible Buffering** – Buffered mode optimizes throughput for files, while unbuffered mode supports real-time paging through [`src/pager.rs`](https://github.com/sharkdp/bat/blob/main/src/pager.rs).

## Frequently Asked Questions

### How does bat determine whether input comes from a file or stdin?

Bat checks the command-line arguments to decide which constructor to call in [`src/bin/bat/input.rs`](https://github.com/sharkdp/bat/blob/main/src/bin/bat/input.rs). If file paths are provided, it uses `new_file_input`; otherwise, it invokes `new_stdin_input`. The resulting `Input` enum variant (`OrdinaryFile` or `StdIn`) then drives the opening logic in `Input::open`.

### What prevents bat from accidentally overwriting the file it is currently reading?

Before opening any file, bat runs an IO-circle check using the `clircle` crate in [`src/input.rs`](https://github.com/sharkdp/bat/blob/main/src/input.rs). This validation compares the input file's device and inode against the output destination. If they match, bat aborts to avoid the infinite loop that would occur in commands like `bat file | bat > file`.

### How does bat handle UTF-16 encoded text files?

During `InputReader::new`, bat uses the `content_inspector` crate to examine the first line's byte patterns. If it detects UTF-16LE or UTF-16BE markers, the `InputReader` automatically transcodes the data to UTF-8 during subsequent `read_line` calls, ensuring the pretty printer receives valid Unicode regardless of source encoding.

### Can bat be used programmatically with custom input sources?

Yes. The `CustomReader` variant of `InputKind` allows library consumers to inject any type implementing `BufRead`. This is used extensively in bat's test suite and is exported publicly in [`src/lib.rs`](https://github.com/sharkdp/bat/blob/main/src/lib.rs) for external crates that need syntax highlighting without file system access.