macOS · Linux · BSD

Spreadsheets, without leaving the terminal

Open a .csv or .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

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