Commit graph

50 commits

Author SHA1 Message Date
John Kerl
1a20b32bb5
join: add --ignore-empty to skip pairing on empty-string join keys (#1194) (#2210)
By default, records with an empty-string value in a join field are
paired just like any other value, so two records both missing an ID
get matched with each other -- rarely the intended behavior.
--ignore-empty treats an empty-string join-field value as if the
field were absent, on both the left and right files, so such records
fall through to unpaired handling (--np/--ul/--ur) instead of
cross-joining on the empty string.

Wires the check through both the default half-streaming join and the
-s/--sorted-input doubly-streaming join, which track left-file
buckets independently and needed the same empty-aware key-presence
check in JoinBucketKeeper.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 16:42:39 -04:00
John Kerl
f17276e0c4
Fix recutils parser for empty-valued fields with "+"-continuation (#2203)
The reader's three-pass pipeline (backslash-join -> fold "+"-continuations
onto raw lines -> split each folded line on ": ") broke on fields written
as bare "Name:" (colon, no trailing space, no value) whose value is
supplied entirely by following "+" lines -- the idiomatic recutils pattern
for e.g. a "%doc:" field. Folding the continuation onto the raw line
destroyed the ": " substring needed for the later split, so parsing such a
field crashed with a "missing field separator" error, even on well-formed
input.

Found by testing against GNU recutils' own tutorial example (books.rec,
from the manual's "A Little Example" page), now added as a fixture.
Restructured parsing to split each line into a key/value field before
folding continuations, so folding happens on the value rather than the
raw line; when the preceding value is empty, the continuation becomes the
value outright instead of gaining a spurious leading newline.

Adds regression coverage (test/cases/io-recutils/0006, 0007) and a docs
section covering the empty-value + continuation pattern.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 14:55:33 -04:00
John Kerl
b7804e0791
Add GNU recutils (.rec) as a Miller I/O format (#2201)
Implements the plan in plans/recutils.md, addressing #378. Adds a
hand-rolled reader/writer pair (pkg/input/record_reader_rec.go,
pkg/output/record_writer_rec.go) supporting recutils' blank-line-separated
"Key: value" records, "+"-continuation and backslash-newline continuation,
and generic comment handling, with recutils' %rec-descriptor schema layer
left unspecialized since Miller has no schema-enforcement concept.

Registers the format under the "recutils" name (--irecutils/--orecutils/
--recutils flags, matching the --idcf/--odcf/--dcf pattern) in the
reader/writer factories and pkg/cli/separators.go, adds docs and a sample
data file, and adds unit tests plus test/cases/io-recutils regression
cases covering round-tripping, continuation lines, comments, and the
hard-error behavior on malformed input.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 11:51:16 -04:00
John Kerl
75275fdcbe
Add named profile sections to .mlrrc, selected via --profile / -P (#358) (#2191)
* Add named profile sections to .mlrrc, selected via --profile / -P (#358)

.mlrrc files may now contain INI-style named sections ("profiles"):

  # Global settings, applied always:
  icsv

  [j]
  ojson
  jvstack

  [tsvout]
  otsv

Lines before any section header are global settings, applied always, so
existing .mlrrc files behave exactly as before. The new main flag
--profile {name} (alias -P {name}) applies the settings from the [name]
section after the global settings; without it, sections are ignored
entirely (their lines aren't even parsed).

It's a fatal error if a requested profile has no matching section in any
.mlrrc file processed, if no .mlrrc file was found at all, or if
--profile is combined with --norc or MLRRC=__none__.

Also fixes the regression-tester to restore per-case environment
variables to their prior values after each case, rather than setting
them to the empty string -- needed so cases pointing MLRRC at a test
file don't clobber the suite-wide MLRRC=__none__ guard.

Includes unit tests, regression-test cases, and doc/man-page updates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Reject --profile / -P within .mlrrc files (#358)

Per maintainer feedback on the PR: profiles are selected on the mlr
command line, not from within a .mlrrc file, so --profile / -P inside a
.mlrrc file is now a parse error -- the same way --prepipe is rejected
there -- rather than being silently ignored.

Adds a unit test, a should-fail regression case, and a docs bullet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 16:49:15 -04:00
John Kerl
f9afae491f
Add mlr rank verb (#2178)
* stats1: add rank accumulator (#383)

Adds `mlr stats1 -a rank` for standard competition ranking (1,2,2,4,...)
on pre-sorted data, most useful with -s for a rank on every record.

* stats1: make rank order-independent by default, add --rank-sorted opt-in fast path

The rank accumulator previously only compared each value to the immediately
preceding record, silently giving wrong ranks for non-adjacent duplicates
(e.g. unsorted input, or interleaved -g groups). Default to correctly
computing standard competition rank from all values seen so far
(order-independent, buffers values, same approach as percentile
accumulators). Add --rank-sorted for callers who can promise sorted input
and want the previous O(1)-space streaming behavior instead.

* Revert "stats1: make rank order-independent by default, add --rank-sorted opt-in fast path"

This reverts commit aa45a591fe.

* Revert "stats1: add rank accumulator (#383)"

This reverts commit 96deed048a.

* Add mlr rank verb (#383)

Reverts the earlier stats1 -a rank / --rank-sorted approach: stats1 is a
reduce verb (many records -> one summary record per group) and rank is a
per-record annotator, so it never fit cleanly there -- it needed stats1's
-s iterative-stats escape hatch just to be useful, plus a bolted-on
sorted/unsorted split.

mlr rank is a dedicated verb modeled on mlr fraction: -f fields to rank,
-g optional group-by, output field <f>_rank. By default it's a two-pass
algorithm (buffers input, like fraction does) giving standard competition
rank (1,2,2,4,...) that's correct regardless of input order. --sorted
opts into a single-pass, O(1)-space streaming alternative for callers who
can promise pre-sorted input (e.g. via 'mlr sort' first).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Regenerate docs/man pages after merging main (sparkline verb)

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 17:35:18 -04:00
John Kerl
5f152d7669
join: exit nonzero when the left file cannot be read (#2169) (#2171)
The join verb's left-file ingest silently swallowed read errors: a
nonexistent or malformed left file produced empty (or partial) output
with exit code 0.

Two defects, one per ingest path:

* Unsorted (half-streaming) path, ingestLeftFile in
  pkg/transformers/join.go: errors from the record-reader's error
  channel were dropped ("TODO: propagate error to caller"), as were
  record-reader construction errors. All left-file read failures were
  silent.

* Sorted (-s) path, readRecord in
  pkg/transformers/utils/join_bucket_keeper.go: the error channel was
  consulted, but the record-reader goroutine sends the error and the
  end-of-stream marker on separate channels, so the select could see
  end-of-stream first and drop the error. A nonexistent left file with
  -s exited 0 about half the time (racy).

Both paths now print "mlr: <error>" to stderr and exit 1, matching how
read errors on regular (right) input files are reported. On receipt of
the end-of-stream marker, both consumers now do a final non-blocking
drain of the error channel, which is deterministic since the reader
sends any error before the marker.

Discovered while verifying #377.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 17:34:33 -04:00
John Kerl
a8ee858e60
reorder -r: group matched fields by regex order, not record order (#2159)
With multiple regexes, 'mlr reorder -r' previously emitted all matched
fields in record order, ignoring the order the regexes were given. Now
matched fields are grouped by regex-list order (first regex's matches
first, then the second's, etc.); within each group, fields keep their
record order. A field matching multiple regexes is claimed by the first
one. This makes 'reorder -r' consistent with 'cut -orf' and satisfies
the original request in #1325: -r '^YYY,^XXX' puts YYY-prefixed fields
first, then XXX-prefixed fields, then the rest. Applies to -e, -b, and
-a modes as well.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 15:07:16 -04:00
John Kerl
3e2eabfd55
Apply join prefixes/rename to unpaired records (#1821) (#2062)
The --lp/--rp prefixes and -j output-field rename were applied only to
paired records, leaving unpaired records emitted via --ul/--ur with their
original (left/right) field names. This broke downstream operations like
unsparsify that expected consistent column names across paired and
unpaired output.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 12:35:58 -04:00
John Kerl
092e4673e5
Fix data dropped from ragged implicit-header TSV/CSV-lite input (#1749) (#2059)
The implicit-header readers for TSV and CSV-lite were missing the
`for` loop in their `nh < nd` branch, so when a data row had more
fields than the auto-generated header, only one extra field was
captured and the rest were dropped. Restore the loop, matching the
explicit-header readers and the pprint reader.
2026-05-17 11:46:59 -04:00
John Kerl
655193732b
Add regexed field-selection to sort-within-records (#1964)
* BROKEN

* add a test case

* fix

* fix natural-sorting issue

* fix silent-drop issue

* test case

* doc update
2026-04-19 11:35:58 -04:00
cobyfrombrooklyn-bot
f20da1953e
fix: stats1 null_count with --fr regex gives wrong results (#1994)
When using --fr (regex field selector) with stats1 -a null_count, void
(empty) field values were unconditionally skipped in
ingestWithValueFieldRegexes, causing null_count to always report 0.

The non-regex path (ingestWithoutValueFieldRegexes) already had a special
case that allows void values through for null_count accumulators. This
commit adds the same exception to the regex path.

Fixes #1639

Co-authored-by: cobyfrombrooklyn-bot <cobyfrombrooklyn-bot@users.noreply.github.com>
2026-04-19 10:52:37 -04:00
John Kerl
4c781739c0
Fix issue #1983 (#2040) 2026-04-19 10:44:53 -04:00
John Kerl
daae7ff7f0
Add DCF (Debian Control Format) as supported file type (#1970)
* reader

* record writer

* pkg/output/record_writer_dcf.go

* test/input/test.dcf

* test/cases/io-dcf/

* pkg/cli/option_parse.go

* make fmt

* make dev

* docs and `make dev`
2026-02-16 11:10:39 -05:00
John Kerl
26600f77aa
Add -r (for regex field-name matching) to mlr nest (#1961)
* initial attempt

* fix up cli flag

* make dev
2026-02-14 19:59:51 -05:00
John Kerl
06e16ea3ee
Don't parse CSV comments (#1859)
* `mlr sort -b` feature

* mlr regtest -p test/cases/cli-help && make dev

* Don't parse CSV comments

* Add tests for PR 1346

* Add tests for PR 1787

* Add test CSV files
2025-08-13 18:07:32 -04:00
Christian G. Warden
df73ad8ec0
Add surv Verb to Estimate a Survival Curve (#1788)
Add a surv verb to estimate a survival curve using Kaplan-Meier.  It
requires duration and status (event or censored) columns, and outputs
each distinct duration and corresponding probability of survival.
2025-05-15 18:17:08 -04:00
John Kerl
cc1cd954ea
Fix unflatten with field names like . .x or x..y (#1735)
* Fix unflatten with field name like `.` `.x` or `x..y`

* docs & test data
2024-12-23 12:27:08 -05:00
John Kerl
1015f18e7b
Fix prepipe handling when filenames have whitespace (#1627)
* Fix prepipe handling when filenames have whitespace

* unit-test data

* Windows-only unit-test item

* Fix Windows fails; neaten
2024-08-25 17:40:07 -04:00
John Kerl
16a898cff4
Fix binary data in JSON output (#1626) 2024-08-25 15:00:51 -04:00
John Kerl
f5eaf290cf
mlr sparsify (#1498)
* mlr sparsify

* regression-test cases

* typofix

* Remove mods due to processor-architecture change
2024-02-18 10:56:26 -05:00
John Kerl
81d11365a0
mlr reorder with regex support [WIP] (#1473)
* mlr reorder with regex support for field-name selection

* neaten

* -r -b/-a; unit-test cases
2024-01-21 15:17:33 -05:00
John Kerl
794a754c36
Support PPRINT barred input (#1472)
* Support PPRINT barred input

* regression-test files

* output from `make dev`

* doc updates
2024-01-20 12:59:12 -05:00
John Kerl
0e3a54ed68
Implement mlr uniq -x (#1457)
* mlr uniq -x

* unit-test cases

* make dev
2023-12-23 16:20:11 -05:00
John Kerl
5b6a1d4713
JSONL output does not properly handle keys with quotes (#1425)
* mlr --l2j, --j2l

* make dev for previous commit

* fix #1424

* unit-test cases

* iterate
2023-11-11 18:58:49 -05:00
John Kerl
d341cc6dd3
DSL functions for summary stats over arrays / maps (#1364)
* DSL stats functions [WIP]

* refactor

* move percentile computation to bifs module; iterate

* mode and antimode

* percentile iterate

* percentile sketching

* neaten

* unit-test iterate

* unify old & new min & max functions

* unit-test cases

* code-dedupe between mode and antimode

* make mode/antimode ties deterministic via first-found-wins rule

* online help strings for new stats DSL functions

* artifacts from `make dev`

* help info on how min/max now recurse into collections

* artifacts from `make dev`

* typofix
2023-08-26 16:02:30 -04:00
John Kerl
2107d520fa
Can't use ${field_name} if it contains UTF-8 characters also encodeable as Latin-1 (#1363)
* unit-test data

* docgen

* windows unit-test accommodations
2023-08-20 12:20:15 -04:00
John Kerl
d4a3bf99b2
Support ZSTD compression in-process (#1360)
* Support ZSTD compression in-process

* doc mods

* unit-test cases

* doc-gen artifacts
2023-08-19 15:22:59 -04:00
John Kerl
b30aceae36
Add %s format specifier for strftime (#1335) 2023-07-04 17:00:02 -04:00
John Kerl
3baebea7a3
Add %N and %O for strfntime (#1334)
* Add `%N` and `%O` for strfntime

* Unit-test mods

* artifacts from `make dev`
2023-07-02 15:49:41 -04:00
John Kerl
d72ef826fb
Add DSL functions for integer nanoseconds since the epoch (#1326)
* DSL functions for 64-bit nano-epoch timestamps

* strfntime

* nsec2gmt; move sec/nsec pairs adjacent to one another

* update on-line help

* artifacts from `make dev`

* unit-test files
2023-06-24 17:05:15 -04:00
John Kerl
59e63272ae
Unify the case verb, and add options (#1306)
* temp

* Unified `case` verb

* unit-test cases

* doc-page update

* make-dev artifacts
2023-06-04 14:38:22 -04:00
John Kerl
d16310ead8
New mlr json-parse -k flag (#1291)
* New `mlr json-parse -k` flag

* docs / make-dev artifacts

* Add unit-test files for the feature

* codespell
2023-05-13 11:22:01 -04:00
John Kerl
b8f3902e6e
Treaet data-file numbers with leading + as numeric (#1269)
* accept data-file numbers with leading + as numeric

* augment unit-test cases

* artifacts from `make dev`
2023-04-15 15:57:22 -04:00
John Kerl
6ea94a002b
Add new upcase and downcase verbs (#1217)
* upcase/downcase verbs

* unit-test cases

* doc files

* remaining `make dev` artifacts: manpage
2023-03-04 01:08:19 -05:00
John Kerl
31fdc1c3ed
Fix #1164: regression on CSV blank-line handling (#1168)
* Fix #1164: regression on CSV blank lines

* unit-test case
2023-01-01 16:49:14 -05:00
John Kerl
b518bf0fe5
mlr unspace verb (#1167)
* mlr unspace verb

* unit tests

* unit tests

* lint
2023-01-01 16:44:06 -05:00
John Kerl
63db1a9059
mlr reshape multiple regex via multiple -r (#1091) 2022-09-05 22:05:54 -04:00
John Kerl
4a53a6b278
Fix labels for mlr histogram --auto (#1089) 2022-09-03 09:55:44 -04:00
John Kerl
2090d0ff5a unit-test case 2022-08-22 09:18:16 -04:00
John Kerl
a64133250c
Fix natsort of empty strings; support mlr sort -rt same as -tr (#1068)
* mlr sort -rt == mlrt sort -tr

* fix natsort of empty strings
2022-08-06 23:10:09 -04:00
John Kerl
2408915160
DSL functions and verbs for UTF-8 <-> Latin-1 (#997)
* latin1_to_utf8 and utf8_to_latin1 DSL functions

* doc-build artifacts for previous commit

* Test cases for latin1_to_utf8 and utf8_to_latin1

* extend on-line help

* latin1_to_utf8 and utf8_to_latin1 verbs

* unit-test cases for verbs

* Keep with kebab-case naming convention for verbs

* webdocs
2022-03-20 17:29:40 -04:00
John Kerl
9b806f9561
Add --left-keep-fields option for mlr join (#967)
* Add --left-keep-fields option for mlr join

* on-line help for mlr join --lk

* doc-build artifacts

* test cases
2022-02-23 23:32:19 -05:00
John Kerl
ca9505dfaf
Natural sort (#932)
* Add natural sort order as an option for the sort verb

* Add natural sort order as an option for the sort DSL function

* doc-build artifacts for on-line help

* webdocs

* codespell fix

* unit-test files for sort verb

* unit-test files for sort DSL function
2022-02-08 00:35:28 -05:00
John Kerl
f7ff63124b
mlr split verb (#898)
* mlr split

* regression-test cases

* doc-build artifacts
2022-01-26 23:16:25 -05:00
John Kerl
77811a4788
Sliding window averages (#894)
* todo

* Neaten existing DSL sketch

* rebase on #893, and sketch

* code-complete

* build artifacts for previous commit

* replace - with _ in shift and slwin
2022-01-23 23:03:46 -05:00
John Kerl
4fb0b716ba
Make is_null/is_not_null DSL functions include new JSON-null type (#883)
* Make is_null/is_not_null DSL functions include new JSON-null type

* regression-test files
2022-01-18 08:25:03 -05:00
John Kerl
eb7e29d207 neaten 2022-01-17 22:57:19 -05:00
John Kerl
8e975c9d39
mlr -O regexp bugfix (#767) 2021-12-03 11:24:19 -05:00
John Kerl
7642f7ede6
Fix mlr -O to return strings for all octal numbers along with 0* numbers like 07 and 08 (#766) 2021-12-01 21:18:19 -05:00
John Kerl
e2b6ec2391
Standardize Go-package structure (#746) 2021-11-11 14:15:13 -05:00