{"id":51411696,"url":"https://github.com/excelano/xled","last_synced_at":"2026-07-04T15:03:08.461Z","repository":{"id":366725874,"uuid":"1277467564","full_name":"excelano/xled","owner":"excelano","description":"sed and awk for tabular data — regex transforms over Excel-style ranges on CSV/DSV","archived":false,"fork":false,"pushed_at":"2026-06-30T19:39:52.000Z","size":162,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-30T21:22:33.812Z","etag":null,"topics":["awk","cli","csv","csv-processing","rust","sed","tabular-data"],"latest_commit_sha":null,"homepage":"https://excelano.com/xled/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/excelano.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-22T23:30:01.000Z","updated_at":"2026-06-30T19:39:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/excelano/xled","commit_stats":null,"previous_names":["excelano/xled"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/excelano/xled","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/excelano%2Fxled","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/excelano%2Fxled/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/excelano%2Fxled/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/excelano%2Fxled/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/excelano","download_url":"https://codeload.github.com/excelano/xled/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/excelano%2Fxled/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35125718,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-04T02:00:05.987Z","response_time":113,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["awk","cli","csv","csv-processing","rust","sed","tabular-data"],"created_at":"2026-07-04T15:03:07.791Z","updated_at":"2026-07-04T15:03:08.456Z","avatar_url":"https://github.com/excelano.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# xled — sed and awk for tabular data\n\nxled brings the muscle memory of `sed` and `awk` to CSV and DSV files. It borrows awk's field model, sed's `s///` substitution, and ed's live in-memory buffer, and points all three at Excel-style ranges: a column by letter or name, a row span, a rectangle, a regex-selected set of cells. You address part of the table, you give it a command, and it shows you the result before anything is written.\n\n**Project page:** [https://excelano.com/xled/](https://excelano.com/xled/) · **Tutorial:** [an introduction](https://excelano.com/xled/tutorial/)\n\n```sh\n# strip the currency formatting from the price column, in place\nxled '[price] s/[$,]//g' products.csv\n\n# derive a tax-inclusive total, rounded like money\nxled '[total] = round(num([price]) * 1.0825, 2)' products.csv\n```\n\n## Why\n\nSpreadsheets that arrive as CSV are full of small, repetitive damage: a dollar sign glued to every number, a leading apostrophe, inconsistent casing, a column that should be split, a header buried under three title rows. The reach for these is usually a throwaway pandas script or a fragile `awk -F,` one-liner that mishandles the first quoted comma. xled is the tool in between: faithful CSV parsing, two-dimensional addressing that matches how you already think about a sheet, and a transform vocabulary small enough to keep in your head.\n\nIt is deliberately not a query engine. xled rewrites cells and reshapes nothing — it never adds or removes rows behind your back, never reorders columns, never coerces a value you didn't ask it to. Join, group, aggregate, and multi-predicate query belong to SQL; xled hands those off to [xql](https://github.com/excelano/xql) rather than growing into them.\n\n## Install\n\n### Debian and Ubuntu\n\nAdd the [Excelano apt repository](https://excelano.com/apt/) once (one-time setup):\n\n```sh\ncurl -fsSL https://excelano.com/apt/setup.sh | sudo sh\n```\n\nThen install it, so `apt upgrade` keeps it current:\n\n```sh\nsudo apt install xled\n```\n\nBoth amd64 and arm64 packages ship with every release.\n\n### Homebrew\n\nOn macOS or Linux, tap and trust the repository once — Homebrew gates third-party taps behind explicit trust (one-time setup):\n\n```sh\nbrew tap excelano/tap\nbrew trust excelano/tap\n```\n\nThen install it, so `brew upgrade` keeps it current:\n\n```sh\nbrew install xled\n```\n\n### Windows\n\nWith [WinGet](https://learn.microsoft.com/windows/package-manager/), so `winget upgrade` keeps it current:\n\n```powershell\nwinget install Excelano.xled\n```\n\nOr run the standalone installer in PowerShell:\n\n```powershell\npowershell -ExecutionPolicy ByPass -c \"irm https://github.com/excelano/xled/releases/latest/download/xled-installer.ps1 | iex\"\n```\n\n### Prebuilt binary (Linux and macOS)\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/excelano/xled/main/install.sh | sh\n```\n\nThe installer downloads the right tarball for your platform from the GitHub release, verifies its checksum, and drops the binary into `~/.cargo/bin` (or the equivalent on Windows). If `xled` isn't found on your `PATH` afterward, ensure `~/.cargo/bin` is on it. Releases also ship raw tarballs (`xled-*.tar.xz` / `.zip`) for manual installation. To uninstall:\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/excelano/xled/main/uninstall.sh | sh\n```\n\nThat removes the binary from `~/.cargo/bin`; you can also just `rm ~/.cargo/bin/xled`.\n\n### Cargo\n\nIf you have a Rust toolchain, install the published crate from [crates.io](https://crates.io/crates/xled). This compiles from source rather than fetching a prebuilt binary, so it is slower than the installer above but needs nothing else:\n\n```sh\ncargo install xled\n```\n\n### Build from source\n\nxled requires only a Rust toolchain. Four pure-Rust crates carry the load (`regex`, `csv`, `clap`, `rustyline`); there are no C dependencies and no runtime.\n\n```sh\ncd xled\ncargo build --release\n```\n\nThe binary is at `target/release/xled`.\n\n## Three ways to run it\n\n```sh\nxled '\u003cscript\u003e' file.csv     # one-shot: run the script, print the result to stdout\n… | xled '\u003cscript\u003e'          # one-shot over piped stdin\nxled file.csv                # open the interactive REPL on a file\n```\n\nIn one-shot mode the data goes to stdout (clean, ready to pipe) and any advisory notices go to stderr, so `xled … file.csv \u003e out.csv` is always safe. The REPL previews edits, keeps an undo stack, and writes only when you tell it to.\n\nA statement is `address command`, one per line. The address picks the cells; the command acts on them. Either part can stand alone: an address by itself shows those cells, and a command with no address acts on the whole table.\n\n## Addresses\n\nPositional addresses are bare; names are bracketed. That one rule resolves every ambiguity a real header throws at you.\n\n| Address | Selects |\n|---|---|\n| `C` | the column at letter C (past Z too: `AA`, `BC`, `CQ`) |\n| `[price]` | the column named `price` — exact, case-sensitive |\n| `3` | row 3 |\n| `2:4` | rows 2 through 4 |\n| `B2:C3` | the rectangle from B2 to C3 |\n| `[price (USD)]` | a name containing spaces, slashes, or parens — brackets quote it |\n| `/active/` | every cell matching the regex |\n| `[status]~/active/` | cells in `[status]` matching the regex |\n| `/active/i [status]` | combine row-select and column to a scoped set |\n\nBrackets disambiguate the hard cases for free: the column *named* `B` is `[B]` while the column *at* letter B is `B`, and the header `2024` is `[2024]` while row 2024 is `2024`. Names match exactly — `[userId]` is not `[userid]` — because a header is data and silent case-folding is the same class of surprise as dropping a leading zero. Add the `i` flag to a regex for a case-insensitive match when you want one.\n\n## Commands\n\n| Command | Does |\n|---|---|\n| `s/re/replacement/flags` | sed substitution over the addressed cells (`g`, `i`, an occurrence number, `\\1`–`\\9`, `\u0026`, `\\U \\L \\u \\l \\E`) |\n| `= expr` | compute a value into one column, creating it if new |\n| `del` | delete whole rows or whole columns |\n| `crop` | reduce the buffer to one rectangle (carve a table out of junk) |\n| `header N` | promote row N to the column-name header |\n| `rename newname` | rename a header in place (takes the rest of the line, no quoting needed) |\n| `fill` / `fill down` | fill blank cells from the value above (merged-cell artifacts) |\n| `drop blanks [rows\\|cols]` | trim empty edge rows and columns |\n| `describe` | advisory region report — preamble, blank edges, suspected header and total rows; never mutates |\n| `show` | print the addressed cells (the default when a command is omitted) |\n\nEach command enforces a scope contract. `= expr` writes exactly one column; `del` takes whole rows xor whole columns, never a partial rectangle; `header` and `rename` take one row or one column. When a command and an address disagree, xled refuses with a correction that names the right form rather than guessing.\n\n## Expressions\n\n`= expr` is the compute layer. Values are one of three types — string, number, bool — and there is **no automatic coercion**: arithmetic requires numbers, and you cast explicitly with `num()` or `bool()`. That is what keeps leading zeros and long identifiers intact. A cast that fails is non-halting: the cell is left untouched and a tally tells you how many were skipped.\n\n```sh\n[total]  = round(num([price]) * [qty], 2)        # arithmetic, money-rounded\n[full]   = [first] \u0026 \" \" \u0026 [last]                # concatenation\n[low]    = num([qty]) \u003c num([reorder])           # a boolean column\n[owner]  = default([owner], \"Unassigned\")        # fill blanks\n[flag]   = if(num([qty]) \u003c num([reorder]), \"REORDER\", \"ok\")\n```\n\nThe library is `num bool len left right mid substr round default coalesce if`. Comparisons are string-wise unless both sides are cast with `num()` — `\"9\" \u003e \"10\"` is true lexically, which is *not* numeric order — because auto-numifying would smuggle back exactly the surprises the stringly model exists to prevent.\n\nNumbers serialize at full `f64` precision, so any currency or fixed-decimal column must be wrapped in `round(…, d)`; xled never rounds on write, because inventing precision the user didn't ask for is the same betrayal as silent coercion.\n\n## Input encoding\n\nxled expects UTF-8. An Excel \"Save as CSV UTF-8\" BOM at the start of the file is stripped so the first column header is not prefixed with it. If the file looks like UTF-7 (the `+ACI-` escape that Scoutbook exports emit) or carries a UTF-16 BOM, xled prints a warning at startup with the `iconv` command needed to convert it to UTF-8 first. UTF-16 fails the underlying read; the warning lets you fix the file instead of staring at a \"stream did not contain valid UTF-8\" error.\n\n## What xled does not do\n\nQuery, join, aggregate, group, and sort are out of scope — that is [xql](https://github.com/excelano/xql) and DuckDB territory, and xled's error messages point you there by name. Reshaping is also out: splitting one cell into several columns, collapsing a multi-row header, unpivoting, merging stacked tables. xled carves *a* rectangle and rewrites cells within the table's existing shape; it is not a splitter and not a spreadsheet.\n\n## Implementation\n\nxled is a hand-written recursive-descent parser over a stringly-typed buffer (`Vec\u003cVec\u003cString\u003e\u003e` with a promotable header overlay), feeding a resolver that turns any address into a set of `(row, column)` coordinates, and an executor that applies each command under its scope contract. The `csv` crate handles the genuinely hard parsing — embedded commas, escaped quotes, embedded newlines — and unchanged cells round-trip byte-for-byte, so leading zeros and quoted fields survive untouched. The `regex` crate powers selection and the `s///` engine, whose sed-faithful replacement dialect (backreferences, `\u0026`, and case-folding) is implemented directly over its captures.\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexcelano%2Fxled","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fexcelano%2Fxled","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexcelano%2Fxled/lists"}