macOS · Linux · BSD

Spreadsheets, without leaving the terminal

Open a .csv, .tsv or Excel .xlsx file where you already are. Aligned columns, real dates, the right delimiter — and a scrolling viewer that appears only when the data won't fit.

exshell Virtual-Edge-Nodes-02212025-1016AM.csv
The exshell interactive viewer showing a CSV of network nodes in aligned columns, with a highlighted cell under the cursor and a status bar reading: Virtual-Edge-Nodes-02212025-1016AM.csv | rows 1-13/13 | col: Node Id

The interactive viewer. Status bar shows the file, your position, and the current column — / to search, Tab to change sheet, q to quit.

Does one thing, properly

A viewer for tabular data — not a spreadsheet. Everything here exists to get a file onto your screen correctly, and then get out of the way.

📊

Reads .xlsx directly

Excel workbooks open without Excel. Switch sheets with Tab, or name one up front with --sheet. Hidden sheets are reachable too, marked rather than skipped.

📅

Dates, not 45291

Spreadsheets store dates as serial numbers. exshell reads the format-applied value, so a date cell shows the date Excel shows — same for currency and percentages.

🔍

Finds the delimiter itself

Comma, semicolon, tab or pipe — detected by parsing candidates and scoring them, not by counting bytes. That semicolon export from a European Excel just opens.

⚡

Pipes like a Unix tool

Redirect it and you get plain aligned text on stdout. grep, head, less -S and friends all work, because the print path never assumes a terminal.

🖥

A viewer only when needed

Ten rows print straight to your scrollback where you can still scroll back to them. A hundred thousand rows opens the full-screen viewer. Override either way with -p or -i.

📦

One static binary

No runtime, no interpreter, no dependencies. Packages for Homebrew, apt and dnf; the macOS build is a signed, notarized universal binary that Gatekeeper accepts silently.

The tools you already have get this wrong

Not for lack of trying — a CSV is a deceptively hard format, and none of the standard tools were built to parse one.

✗ column -t

$ column -t -s, contacts.csv
name          note              email
Ada Lovelace  "Analytical       Engine, 1843"  ada@…
Alan Turing   "Bletchley        Park, 1939"    alan@…

A quoted field containing a comma is split anyway — the row shifts, and every column after it is wrong. column splits on bytes; it has no idea what a quote means.

✓ exshell

$ exshell contacts.csv
name          note                       email
Ada Lovelace  Analytical Engine, 1843    ada@…
Alan Turing   Bletchley Park, 1939       alan@…

The quoted comma stays inside its field, where it belongs. Ragged rows, embedded newlines and BOMs are handled the same way — quietly, and correctly.

✗ cat

$ cat export.csv
Naam;Datum;Bedrag
Vermeer;45291;1234,56
Rembrandt;45292;987,65

Semicolons because the file came from a European Excel. Serial numbers because that is how a spreadsheet stores a date. Neither is readable, and .xlsx is not readable at all.

✓ exshell

$ exshell export.csv
Naam       Datum        Bedrag
Vermeer    2024-01-15     1234,56
Rembrandt  2024-01-16      987,65

Delimiter sniffed, dates rendered as dates, numeric columns right-aligned. No flags — though --delim, --encoding and --no-header are there when a file needs them.

One command, most places

Signed packages from a signed repository. Pick your platform.

$ brew tap vanlabeke/tap
$ brew trust --formula vanlabeke/tap/exshell
$ brew install exshell

Homebrew 6 asks you to trust anything from a tap outside homebrew-core, formulae included — it is asking whether you vouch for a third party running its own install code. A fair question, and worth answering deliberately.

The macOS build is a universal binary (Apple Silicon and Intel in one file), code-signed with a Developer ID and notarized by Apple, so it runs without a Gatekeeper warning.

$ sudo install -d /etc/apt/keyrings
$ curl -fsSL https://pkg.vanlabeke.dev/apt/gpg.key \
    | sudo tee /etc/apt/keyrings/vanlabeke.asc > /dev/null
$ echo "deb [signed-by=/etc/apt/keyrings/vanlabeke.asc] \
    https://pkg.vanlabeke.dev/apt stable main" \
    | sudo tee /etc/apt/sources.list.d/vanlabeke.list
$ sudo apt update && sudo apt install exshell

amd64 and arm64. The repository metadata is GPG-signed and the packages are signed individually, so apt verifies both.

$ sudo curl -fsSL -o /etc/yum.repos.d/vanlabeke.repo \
    https://pkg.vanlabeke.dev/yum/vanlabeke.repo
$ sudo dnf install exshell

x86_64 and aarch64, with gpgcheck and repo_gpgcheck both on. Release candidates never enter this channel — only stable tags are published here.

# FreeBSD, OpenBSD and NetBSD — amd64 and arm64
$ curl -fsSLO https://github.com/vanlabeke/exshell/releases/latest/\
download/exshell_<version>_freebsd_amd64.tar.gz
$ tar xzf exshell_*.tar.gz
$ sudo install -m 0755 exshell /usr/local/bin/

No native port yet. Submitting to a ports tree means a human doing it, which is not something a release pipeline can do on its own — so for now it is the tarball from the releases page.

$ git clone https://github.com/vanlabeke/exshell
$ cd exshell
$ make install           # -> /usr/local/bin

# or somewhere you already own — no sudo at all
$ make install PREFIX=$HOME/.local

Requires Go 1.26 or later. Run it as plain make install, not sudo make install — under sudo the build itself runs as root and leaves root-owned files in your module cache, which breaks later builds as yourself. Only the copy into /usr/local/bin needs elevation, and it will ask.

Keys worth knowing

Vim motions where you'd expect them, arrows where you wouldn't. Press ? in the viewer for the same list without leaving the terminal.

h j k l Move the cursor (arrow keys too)
ctrl+f ctrl+b Page down / up (or PgDn / PgUp)
g G Jump to first / last row
Home End Jump to first / last column
/ Search every cell — Enter runs, Esc cancels
n N Next / previous match
Enter Inspect the full, untruncated cell value
Tab shift+Tab Next / previous sheet
? Toggle the in-app help screen
q ctrl+c Quit

Hand your agent a spreadsheet

Coding agents live in a shell, and they are sent the same CSV and Excel files you are. exshell gives them a single command that turns any of those files into plain, aligned text they can read.

$ exshell --list-sheets report.xlsx
Summary
Orders
Archive

$ exshell --print --sheet Orders report.xlsx | head -4
order_id  date        customer       total
--------  ----------  -------------  -------
   10021  2026-09-01  Vermeer B.V.   1234.56
   10022  2026-09-02  Rembrandt Ltd  987.65

$ exshell --print export.tsv | grep -i overdue

Machine-readable reference: llms.txt · drop-in agent skill: SKILL.md

  • Nothing is lost when piped. Without a terminal, exshell never truncates a cell and never opens the viewer, so the full value of every field reaches the agent.
  • One tool for every format. CSV, TSV, semicolon and pipe-delimited text, .xlsx and .xlsm, detected from content, not just the extension.
  • Values as a human sees them. Dates, currency and percentages come out formatted, not as 45291 or 0.125, so the model reasons about the right numbers.
  • Predictable exit codes. 0 success, 1 unreadable or encrypted file, 2 bad flags or sheet. Errors go to stderr, in plain English.
  • Read-only by design. exshell cannot modify a file, so letting an agent run it is safe.

Common questions

How do I view an Excel .xlsx file in the terminal?

Run exshell file.xlsx. It reads the workbook directly, with no Excel or LibreOffice needed. --list-sheets shows the sheet names, and --sheet <name|#> opens a specific one.

Can exshell open TSV and semicolon-separated files?

Yes. Comma, semicolon, tab and pipe delimiters are detected automatically by parsing each candidate and scoring the result, so TSV files and European Excel exports open without flags. --delim overrides the detection when you need it to.

Can an AI agent or script use exshell to read spreadsheet data?

Yes, and it was built to pipe. When stdout isn't a terminal, exshell always writes plain aligned text and never truncates cells, so an agent can run exshell --print file.xlsx and read the result. See the agent reference for the details agents need.

Why does my spreadsheet show 45291 instead of a date?

Excel stores dates as serial numbers and applies a display format on top. exshell reads the format-applied value, so a date cell shows the same date Excel shows.

Does it support the old .xls format or password-protected workbooks?

No. Legacy pre-2007 .xls files and encrypted workbooks fail with a clear error and exit code 1. Re-save a .xls as .xlsx first.

What it deliberately isn't

exshell is a viewer. There is no editing, no formulas, no sorting and no filtering — it shows you what is in the file, and changing the file is someone else's job. Sorting is sort; filtering is grep; both already work, because the output pipes.

The whole file is held in memory, which is right for the exports people actually email around and wrong for a multi-gigabyte log. Merged cells show their value only in the top-left cell, because that is genuinely where the file stores it. The viewer is keyboard-only. These are choices, and they are written down rather than discovered.

Try it on the next file you're sent

Free and open source under the BSD 3-Clause license.

brew install exshell