A fast, single-binary CSV transformation CLI for Unix-style pipelines. Pipe CSVs through filter, derive, summarize, and head — each command reads stdin, writes stdout, logs to stderr.
For local inspection workflows, csvzall view <file.csv> starts a read-only browser table view backed by the same CSV parser.
Useful links:
- csvzall Browser opens and edits local CSVs in the browser through the experimental browser editor.
- obsidian-csvzall integrates csvzall workflows with Obsidian notes.
- Source repository hosts the CLI, native viewer, and browser editor code.
Compute estimated 1-rep maxes from a FitNotes export using the Epley formula, then show the best set per exercise:
csvzall filter "Weight > 0 && Reps > 0" FitNotes_Export.csv \
| csvzall derive "Epley1RM = Weight * (1 + Reps / 30)" - \
| csvzall summarize --group-by Exercise --max Epley1RM --show Date - \
| csvzall head -+-----------------------+------------+------------------+
| Exercise | Date | max_Epley1RM |
+-----------------------+------------+------------------+
| Overhead Press | 2025-03-07 | 110.833333333333 |
| Flat Barbell Bench | 2024-04-20 | 175.666666666667 |
| Deadlift | 2026-01-18 | 302.5 |
| ... | ... | ... |
+-----------------------+------------+------------------+
Let a host script own API pagination and authentication, then hand deterministic tabular steps to csvzall:
# 1. Extract API-shaped JSON into incoming CSV.
csvzall json extract todoist-activities.json --map todoist-map.json > incoming.csv
# 2. Rerunnable keyed import: existing rows win, duplicates are skipped.
csvzall merge activity-store.csv incoming.csv --key event_id --in-place
# 3. Inspect the source table in a browser when Markdown tables get cramped.
csvzall view activity-store.csv
# 4. Produce an Obsidian-ready Markdown summary table.
csvzall sql query --csv activity-store.csv --format markdown --sql \
"SELECT substr(completed_at, 1, 7) AS month, COUNT(DISTINCT due_date) AS days FROM data GROUP BY month ORDER BY month" \
> monthly-summary.md
# 5. Render a calendar heatmap SVG.
csvzall heatmap activity-store.csv --date due_date --start 2026-01-01 --end 2026-12-31 \
--title "Gym Attendance" > gym-heatmap.svgKeep rows where the expression evaluates to non-zero. Supports arithmetic, comparisons (>, <, >=, <=, ==, !=), and boolean logic (&&/and, ||/or).
Column matching is case-insensitive by default. Use --exact to require exact case-sensitive column names.
csvzall filter "Salary > 50000 && Department == 1" employees.csv
csvzall filter --exact "Salary > 50000 && Department == 1" employees.csv
csvzall filter "Score >= 90 || Bonus > 0" - # read from stdinAdd a new column computed from an expression. The expression can reference any existing numeric column.
Column matching is case-insensitive by default. Use --exact to require exact case-sensitive column names.
csvzall derive "Tax = Salary * 0.22" employees.csv
csvzall derive --exact "Tax = Salary * 0.22" employees.csv
csvzall derive "BMI = Weight / (Height * Height)" -Group rows and compute per-group aggregates. The row that produced the winning value is retained, so --show columns (e.g. a date or label) come from that row.
--group-by <col> Column to group on (required)
--max <col> Column to compute the maximum of (required)
--show <col> Additional columns to carry from the winning row (repeatable)
--exact Use exact case-sensitive column matching
csvzall summarize --group-by Region --max Revenue --show Quarter sales.csv
csvzall summarize --exact --group-by Region --max Revenue --show Quarter sales.csvOutput column is named max_<col>.
Print the header and first N rows as a formatted ASCII table. Useful for inspecting pipeline output.
-n, --rows <N> Number of data rows to show (default: 50)
csvzall head -n 10 data.csv
some-command | csvzall head -Extract rows from JSON into stable CSV using an explicit mapping file. This is
not a general JSON query command; it supports a small deterministic path subset:
$, .field, ["field name"], ['field name'], [0], and [*].
{
"rows": "$.results[*]",
"columns": {
"event_id": "$.id",
"event_type": "$.event_type",
"completed_at": "$.event_date",
"content": "$.extra_data.content",
"due_date": "$.extra_data.due_date",
"was_overdue": "$.extra_data.was_overdue"
}
}csvzall json extract todoist_activities.json --map todoist_activity_map.json > incoming.csv
csvzall merge todoist_activity_store.csv incoming.csv --key event_id --in-placeOptional, missing, and null values become empty CSV cells. Nested API payload
paths such as $.extra_data.content are evaluated relative to each selected
row. Scalars are emitted as stable text; objects and arrays selected as column
values are rejected.
Query CSV directly with SQLite SQL. CSV input is loaded into a table named
data by default; override it with --table <name>. Use --csv - to read
CSV from stdin, or --db <path> to query an existing SQLite database file.
Query results are streamed as CSV to stdout by default. Pass
--format markdown for a deterministic, escaped Markdown table suitable for
Obsidian notes. When loading CSV, csvzall infers SQLite column affinity with
csv-parser: numeric columns stay numeric for comparisons and arithmetic, while
text-like columns and very large integer-looking identifiers are stored as
TEXT so row projections preserve exact CSV values.
csvzall sql query --csv gym-attendance.csv --sql \
"SELECT substr(date, 1, 7) AS month, COUNT(*) AS attendance_days FROM data GROUP BY month ORDER BY month"
csvzall sql query --csv gym-events.csv --sql \
"SELECT COUNT(DISTINCT date) AS attendance_days FROM data"
csvzall sql query --csv gym-attendance.csv --format markdown --sql \
"SELECT substr(date, 1, 7) AS month, COUNT(*) AS attendance_days FROM data GROUP BY month ORDER BY month"SQLite-backed commands support regular expressions through both the REGEXP
operator and regexp_like(value, pattern). Prefix a pattern with (?i) for
case-insensitive matching.
csvzall sql query --csv gym-events.csv --sql \
"SELECT content FROM data WHERE regexp_like(content, '(?i)\b(gym|workout|lift|weights)\b')"Stream a CSV and print the maximum value in one column without loading SQLite. Numeric cells compare as numbers through csv-parser's scalar classification; other scalar text, including ISO timestamp strings, compares deterministically as text.
csvzall max todoist_activities.csv --column completed_atCompanion to max: stream a CSV and print the minimum value in one column with
the same numeric and deterministic text comparison behavior.
csvzall min todoist_activities.csv --column completed_atStart a local-only read-only HTTP viewer for one plain local CSV file. The
server binds to 127.0.0.1, prints the full viewer URL to stdout, and opens a
browser by default unless --no-open is passed. API requests are gated by a
random session token, and the file path is fixed for the lifetime of the
process. Pass --startup-json to print {"url":"http://127.0.0.1:..."} for
host integrations such as
obsidian-csvzall.
The viewer builds a compact row-offset index and serves rows through paged
/api/rows?offset=...&limit=... requests for all file sizes instead of loading
the whole table in the server. AG Grid Community handles column resizing and
virtual scrolling, while the local API (/api/schema, /api/rows,
/api/health) keeps the default experience read-only by design. Viewer HTML,
CSS, JavaScript, and AG Grid assets are embedded in the binary. The view is a
startup-time snapshot of the CSV; reopen the viewer to pick up on-disk file
changes.
For viewer development, pass --viewer-assets <dir> or set
CSVZALL_VIEWER_ASSETS=<dir> to serve first-party index.html, viewer.css,
and viewer.js from disk on every request. AG Grid and Popright remain
embedded.
Pass --edit to enable explicit editable mode. Editable mode uses the same
row-offset index, keeps unsaved cell/row/column changes in an overlay, tracks
dirty state, supports reset from disk, and saves by writing a temporary sibling
CSV before atomically replacing the source. Save refuses if the source file size
or mtime changed after the viewer opened.
Current limitation: view is optimized for plain local CSV files. stdin,
.gz, and .zip inputs are rejected in this pass. Server-side global
sort/search/filter are deferred for paged mode. Edit mode uses the same paged
row loading path and applies visible-row edits through the backend overlay.
csvzall view todoist_activities.csv
csvzall view todoist_activities.csv --edit
csvzall view todoist_activities.csv --no-open --port 43117
csvzall view todoist_activities.csv --no-open --startup-json
csvzall view todoist_activities.csv --view-mode paged
csvzall view todoist_activities.csv --viewer-assets src/viewerRender fixed-shape date,content CSV as plain Markdown month tables suitable
for Obsidian notes. The input must contain exact date and content columns;
date must be ISO YYYY-MM-DD. Duplicate dates are rejected.
csvzall calendar habit_days.csv --start 2026-05-01 --end 2026-05-31
csvzall calendar habit_days.csv --start 2026-05-01 --end 2026-06-30 \
--month-header "{month-name} {year}"Additional columns are ignored. Cells outside the requested range or outside the
current month are left empty, and the content value is used as the date cell
body.
Render dated CSV rows as a self-contained SVG calendar heatmap. The input must
contain a date column. Dates may be YYYY-MM-DD, American M/D/YYYY or
M-D-YYYY, or European D/M/YYYY or D-M-YYYY. Ambiguous numeric dates use a
column-wide inference when an unambiguous clue exists; otherwise they are read
month-first. If --value is omitted, each row contributes 1; otherwise the
named numeric column is summed per date. Use --label to include cell tooltip
text.
csvzall heatmap gym-attendance.csv --start 2025-05-15 --end 2026-05-15 \
--date date --title "Gym Attendance" > gym.svg
csvzall heatmap daily-counts.csv --start 2026-01-01 --end 2026-12-31 \
--date day --value count --label note > heatmap.svgDuplicate dates are aggregated, rows outside the requested range are ignored by
the chart renderer, and the SVG is written to stdout so it can be redirected or
piped like any other csvzall command. The command is built when csvzall is
configured with a local svgplot checkout.
Run configured generated artifacts from .csvzall/charts.json. Existing chart
types render SVG (heatmap, bar, line); markdown-table renders an
escaped Markdown table note that can be embedded in Obsidian with
![[path/to/output]]. Relative input and output paths resolve against the
vault or config root, and runOnSave lets companion integrations such as
obsidian-csvzall
regenerate the artifact when the source CSV changes.
{
"charts": [
{
"id": "monthly-summary",
"type": "markdown-table",
"input": "activity-store.csv",
"output": "Reports/generated/monthly-summary.md",
"runOnSave": true,
"options": {
"sql": "SELECT substr(completed_at, 1, 7) AS month, COUNT(*) AS days FROM data GROUP BY month ORDER BY month"
}
},
{
"id": "recent-events",
"type": "markdown-table",
"input": "activity-store.csv",
"output": "Reports/generated/recent-events.md",
"options": {
"columns": ["completed_at", "content", "due_date"]
}
}
]
}If neither sql nor columns is provided, markdown-table exports all CSV
columns. Pass --validate to check selected configs without writing output.
Use csvzall charts schema for the full generated config reference, including
per-chart option keys and accepted value forms such as heatmap lookback.
Append one CSV to another after validating that headers match exactly. Without
--in-place, the combined CSV is written to stdout. With --in-place, csvzall
writes a temporary sibling file and replaces the original only after validation
and output writing succeed. append does not inspect keys or deduplicate rows;
use merge for rerunnable keyed imports.
csvzall append existing.csv incoming.csv > combined.csv
csvzall append existing.csv incoming.csv --in-placeMerge incoming rows into an existing CSV for rerunnable local imports. Headers must match exactly. The key column must exist in both files. Duplicate keys within existing fail, duplicate keys within incoming fail, and incoming rows whose key already exists are skipped so existing rows win.
Without --in-place, the merged CSV is written to stdout. With --in-place,
csvzall writes a temporary sibling file and replaces the original only after
validation and output writing succeed. Added/skipped counts are reported on
stderr unless --quiet is set.
csvzall merge activity-store.csv incoming.csv --key event_id > merged.csv
csvzall merge activity-store.csv incoming.csv --key event_id --in-placeInfer the PostgreSQL schema that postgres export would use, without connecting to a database or loading rows. This is useful for inspecting type inference and timing inference separately from COPY.
csvzall infer vehicles.csv --table used_cars --verboseExport CSV rows into PostgreSQL with full-file schema inference followed by COPY.
csvzall postgres vehicles.csv --dbname postgres --user postgres --table used_carsFiles ending in .gz are read as gzip-compressed CSV automatically:
csvzall head vehicles.csv.gz
csvzall postgres vehicles.csv.gz --dbname postgres --user postgres --table used_carsFiles ending in .zip are read as ZIP-compressed CSV automatically when the
archive contains exactly one file. For archives with multiple files, pass
--zip-entry <name> to select the CSV member:
csvzall head vehicles.zip
csvzall head archive.zip --zip-entry exports/vehicles.csvCredential storage is optional. Save a PostgreSQL password to the OS keychain:
csvzall postgres --save --host localhost --port 5432 --dbname postgres --user postgresNormal postgres runs try the keychain first, then prompt with masked input if
no credential is found. --password remains available for automation but prints
a warning because command-line passwords are visible in shell history and process
lists. --password-env VARNAME reads the password from an environment variable.
Remove stored PostgreSQL credentials:
csvzall forget postgres
csvzall forget postgres --host localhost --port 5432 --dbname postgres --user postgres--copy-batch-rows <N> tunes the producer batch size for the COPY pipeline. The default is 10000, which keeps memory bounded well on large files; larger values may increase peak memory without improving throughput.
--parallel-copy [N] runs multiple PostgreSQL COPY workers, each with its own
connection. This can improve throughput when a single COPY stream is the
bottleneck, but physical insertion order is not preserved. The default is 1
when the flag is omitted. Passing --parallel-copy without a value uses
min(hardware_concurrency / 2, 8), and explicit values are capped at hardware
concurrency.
There is no built-in pipeline format. Save complex chains as PowerShell, Bash, or batch scripts — they're already shareable, version-controllable, and composable with the rest of your toolbox.
Requires CMake 3.25+ and a C++23 compiler. A local csv-parser checkout is preferred for development, the in-repo submodule is used for CI/release builds, and CMake can fetch a pinned fallback when no local checkout or submodule is configured.
# csv-parser can live at ../csv-parser, external/csv-parser, or a custom path.
# When both local paths exist, ../csv-parser is preferred by default.
cmake -S . -B build
cmake --build build --config ReleaseTo specify a custom csv-parser location:
cmake -S . -B build -DCSV_PARSER_ROOT=/path/to/csv-parserTo enable SVG chart output, install svgplot, keep it as a sibling checkout, let
CMake fetch it from GitHub, or pass its location explicitly:
cmake -S . -B build -DSVGPLOT_ROOT=/path/to/svgplotThe binary is at build/Release/csvzall.exe (Windows) or build/csvzall (Linux/macOS).
The C++ implementation is also built as csvzall_core, with the alias target
csvzall::core, and the CLI executable links that library from a thin
main.cpp layer. This is an early library target for custom C++ programs and
in-tree tests; the installed SDK/header surface is still intentionally rough.
If Doxygen is installed, generate local API documentation with:
cmake --build build --target docsThe generated HTML is written to docs/doxygen/html. The GitHub Pages
documentation workflow generates the same site from Doxyfile on pushes to
main or master.
Windows users can build, install, and add csvzall to PATH with:
.\scripts\Install-csvzall.ps1By default this installs to C:\Program Files\csvzall, adds
C:\Program Files\csvzall\bin to the machine PATH, and relaunches with a
Windows UAC prompt if Administrator rights are required. The installer refuses
to install a build without SVG chart support because csvzall view and
companion integration chart workflows rely on the charts command.
For a per-user install that does not require elevation:
.\scripts\Install-csvzall.ps1 -InstallPrefix "$env:LOCALAPPDATA\csvzall" -PathScope UserFor intentionally minimal installs without chart rendering, pass -AllowNoSvg.
Release builds publish the full CLI artifacts for each desktop platform.
Releases also publish slim Obsidian helper artifacts named
csvzall-<version>-obsidian-<platform>-<arch>.<ext> for native desktop
platforms. Those helpers are intended for the Obsidian plugin's managed install
path and keep CSV viewing/editing, SVG charting, and SQLite-backed workflows,
but disable PostgreSQL export, OS keychain integration, and compressed
.gz/.zip CSV input. Obsidian helper artifacts are checked so they package
only the native csvzall executable and no bundled runtime shared libraries.
They also force pinned in-tree dependency sources for simdjson, nlohmann/json,
and svgplot instead of using system packages that happen to be installed on the
release runner.
| Library | Author / maintainer | Role | How it's sourced |
|---|---|---|---|
| csv-parser | Vincent La | CSV parsing, writing, and scalar type classification | Local checkout preferred; in-repo submodule for CI/release builds; pinned FetchContent fallback |
| simdjson v3.13.0 | Daniel Lemire, Geoff Langdale, and contributors | JSON parsing for mapping-driven json extract |
System package if CSVZALL_PREFER_SYSTEM_SIMDJSON=ON; FetchContent fallback; release workflows force FetchContent/static linkage |
| JSON for Modern C++ v3.12.0 | Niels Lohmann and contributors | JSON serialization and configuration helpers | System package if CSVZALL_PREFER_SYSTEM_NLOHMANN_JSON=ON; FetchContent fallback; release workflows force FetchContent |
| argparse v3.1 | Pranav | CLI argument parsing | FetchContent |
| indicators v2.3 | Pranav | Terminal progress bars for long-running imports | FetchContent |
| zlib v1.3.1 | Mark Adler and contributors | Optional gzip and ZIP/deflate decompression for compressed CSV inputs | System package if available; FetchContent fallback; omitted when CSVZALL_ENABLE_COMPRESSED_INPUT=OFF |
| keychain v1.3.1 | hrantzsch | Optional OS credential storage for PostgreSQL passwords | System package if available; FetchContent fallback; Linux requires libsecret |
| SQLiteCpp v3.3.2 | Sébastien Rombauts | SQLite C++ wrapper using bundled SQLite | FetchContent, with a local CMake patch |
| libpqxx v7.10.1 | Jeroen T. Vermeulen | PostgreSQL C++ client API used by the postgres command |
System package if available; FetchContent fallback |
| PostgreSQL libpq | PostgreSQL Global Development Group | PostgreSQL client C library required by libpqxx | System PostgreSQL installation |
| Library | Author / maintainer | Role | How it's sourced |
|---|---|---|---|
| svgplot v0.4.0 | Vincent La | SVG chart rendering for heatmap, bar, and line chart outputs |
CMake package if CSVZALL_PREFER_SYSTEM_SVGPLOT=ON; local checkout via SVGPLOT_ROOT or sibling ../svgplot; pinned FetchContent fallback; release workflows force local or FetchContent sources |
| Library | Author / maintainer | Role | How it's sourced |
|---|---|---|---|
| cpp-httplib v0.18.5 | Yuji Hirose and contributors | Embedded local HTTP server for the view command |
Vendored single header under vendor/httplib |
| AG Grid Community v32.3.9 | AG Grid Ltd. | Interactive browser table for the view command |
Vendored browser assets under vendor/ag-grid, embedded into csvzall at build time |
| Popright v0.1.2 | Vincent La | Context menu and dropdown menu primitives for the native view command and csvzall Browser web app |
Vendored npm package under vendor/popright, embedded into csvzall at build time and linked into the browser editor Vite app via a local file dependency |
| Tabler Icons | Paweł Kuna and contributors | Viewer toolbar and context menu icons | Selected inline SVG paths in src/viewer/viewer.js |
| Library | Author / maintainer | Role | How it's sourced |
|---|---|---|---|
| AG Grid Community ^32.3.3 | AG Grid Ltd. | Interactive grid for the experimental csvzall Browser web app | npm dependency under src/viewer_wasm/web |
| Vite ^6.0.0 | Evan You and contributors | Bundles csvzall Browser for local preview and GitHub Pages deployment | npm dev dependency under src/viewer_wasm/web |
| Emscripten | Emscripten contributors | Builds the experimental browser CSV editor release asset | Local Emscripten SDK for development; GitHub Actions clones emscripten-core/emsdk for CI/release builds |
| Library | Author / maintainer | Role | How it's sourced |
|---|---|---|---|
| Catch2 v3.4.0 | Catch2 contributors | Test framework | FetchContent, tests only |
| gcovr | gcovr contributors | Coverage report generation for CI | GitHub Actions coverage workflow only |
| Library | Author / maintainer | Role | How it's sourced |
|---|---|---|---|
| actions/cache v5.0.5 | GitHub | Cache CMake FetchContent dependencies in CI | GitHub Actions workflows only |
| lukka/get-cmake v4.3.3 | Luca K. and contributors | Install CMake for release binary builds | GitHub Actions release workflow only |
| Codecov GitHub Action v6.0.1 | Codecov | Upload coverage reports to Codecov | GitHub Actions coverage workflow only |
| Library | Author / maintainer | Role | How it's sourced |
|---|---|---|---|
| Doxygen | Dimitri van Heesch and contributors | Generate C++ API documentation | Optional local tool; installed in the GitHub Pages documentation workflow |
| actions/configure-pages v5 | GitHub | Configure GitHub Pages metadata for API docs publishing | GitHub Actions documentation workflow only |
| actions/upload-pages-artifact v4 | GitHub | Upload generated Doxygen HTML for GitHub Pages deployment | GitHub Actions documentation workflow only |
| actions/deploy-pages v4 | GitHub | Deploy generated Doxygen HTML to GitHub Pages | GitHub Actions documentation workflow only |
- Data flows on stdout, diagnostics on stderr. Every command is safe to pipe.
-as the input path means stdin. All commands accept it.filterandderiveuse SQL syntax — standardWHEREclauses and SQL expressions.- SQLite-backed commands support
REGEXPandregexp_like(value, pattern); NULL input does not match and invalid patterns fail the query. mergeis the keyed rerunnable import primitive;appendis only exact-header concatenation.- Column matching is case-insensitive by default (SQLite identifier resolution).
calendarconsumes fixed-shapedate,contentCSV, rejects duplicate dates, and renders locale-independent Sunday-first month tables.heatmapconsumes generic dated CSV, aggregates duplicate dates, and renders a self-contained SVG through svgplot when that library is configured.
csvzall is licensed under the MIT License. See LICENSE.
