From 86c8097dc26fdef8498f3c1b99a50fb1ae958661 Mon Sep 17 00:00:00 2001 From: John Kerl Date: Sun, 17 May 2026 12:13:21 -0400 Subject: [PATCH] Default to "cat" verb when none is supplied (#2060) * Default to "cat" verb when none is supplied (#2029) Invocations like 'mlr --j2y' or 'mlr --c2p' previously failed with "no verb supplied", forcing users to type the trailing 'cat' explicitly for pure format conversions. Default the verb to 'cat' in that case. Bare 'mlr' with no flags, no verb, and no files still prints the main usage banner. This handles flag-only invocations (e.g. 'mlr --c2j < input.csv' or 'mlr --c2j --from input.csv'). File names without a preceding verb are still parsed as verb candidates and continue to error if not found; that broader change is out of scope here. * Use ${MLR} substitution for bare-mlr regression case (#2029) The regtester only substitutes the mlr executable when the cmd starts with "mlr " (with a trailing space). The bare-mlr usage-banner test had a cmd of just "mlr", so on CI -- which invokes regtest with a relative path like 'test/../mlr' -- the test shelled out to a literal 'mlr' that isn't on PATH and failed with exit 127. Switch the cmd to ${MLR} (the regtester's explicit substitution token) so the case runs the right binary in any invocation context. --- docs/src/keystroke-savers.md | 202 ++++++++++++++++++ docs/src/keystroke-savers.md.in | 14 ++ docs/src/manpage.md | 8 +- docs/src/manpage.txt | 8 +- docs/src/online-help.md | 5 +- docs/src/reference-main-overview.md | 2 + docs/src/reference-main-overview.md.in | 2 + docs/src/reference-verbs.md | 24 ++- docs/src/reference-verbs.md.in | 8 +- man/manpage.txt | 8 +- man/mlr.1 | 9 +- pkg/climain/mlrcli_parse.go | 11 +- pkg/terminals/help/entry.go | 5 +- test/cases/cli-default-verb-cat/0001/cmd | 1 + test/cases/cli-default-verb-cat/0001/experr | 0 test/cases/cli-default-verb-cat/0001/expout | 3 + test/cases/cli-default-verb-cat/0001/input | 1 + test/cases/cli-default-verb-cat/0002/cmd | 1 + test/cases/cli-default-verb-cat/0002/experr | 0 test/cases/cli-default-verb-cat/0002/expout | 3 + test/cases/cli-default-verb-cat/0002/input | 3 + test/cases/cli-default-verb-cat/0003/cmd | 1 + test/cases/cli-default-verb-cat/0003/experr | 0 test/cases/cli-default-verb-cat/0003/expout | 92 ++++++++ test/cases/cli-default-verb-cat/0004/cmd | 1 + test/cases/cli-default-verb-cat/0004/experr | 13 ++ test/cases/cli-default-verb-cat/0004/expout | 0 .../cli-default-verb-cat/0004/should-fail | 0 28 files changed, 408 insertions(+), 17 deletions(-) create mode 100644 test/cases/cli-default-verb-cat/0001/cmd create mode 100644 test/cases/cli-default-verb-cat/0001/experr create mode 100644 test/cases/cli-default-verb-cat/0001/expout create mode 100644 test/cases/cli-default-verb-cat/0001/input create mode 100644 test/cases/cli-default-verb-cat/0002/cmd create mode 100644 test/cases/cli-default-verb-cat/0002/experr create mode 100644 test/cases/cli-default-verb-cat/0002/expout create mode 100644 test/cases/cli-default-verb-cat/0002/input create mode 100644 test/cases/cli-default-verb-cat/0003/cmd create mode 100644 test/cases/cli-default-verb-cat/0003/experr create mode 100644 test/cases/cli-default-verb-cat/0003/expout create mode 100644 test/cases/cli-default-verb-cat/0004/cmd create mode 100644 test/cases/cli-default-verb-cat/0004/experr create mode 100644 test/cases/cli-default-verb-cat/0004/expout create mode 100644 test/cases/cli-default-verb-cat/0004/should-fail diff --git a/docs/src/keystroke-savers.md b/docs/src/keystroke-savers.md index ec15e9308..e8fbc9073 100644 --- a/docs/src/keystroke-savers.md +++ b/docs/src/keystroke-savers.md @@ -57,6 +57,208 @@ red square true 2 15 79.2778 0.0130 You can get the full list [here](file-formats.md#data-conversion-keystroke-savers). +## Omitting the verb for format conversions + +The verb defaults to [`cat`](reference-verbs.md#cat), so for pure format conversions you can leave the verb off entirely: + +
+mlr --c2j cat example.csv
+
+
+[
+{
+  "color": "yellow",
+  "shape": "triangle",
+  "flag": "true",
+  "k": 1,
+  "index": 11,
+  "quantity": 43.6498,
+  "rate": 9.8870
+},
+{
+  "color": "red",
+  "shape": "square",
+  "flag": "true",
+  "k": 2,
+  "index": 15,
+  "quantity": 79.2778,
+  "rate": 0.0130
+},
+{
+  "color": "red",
+  "shape": "circle",
+  "flag": "true",
+  "k": 3,
+  "index": 16,
+  "quantity": 13.8103,
+  "rate": 2.9010
+},
+{
+  "color": "red",
+  "shape": "square",
+  "flag": "false",
+  "k": 4,
+  "index": 48,
+  "quantity": 77.5542,
+  "rate": 7.4670
+},
+{
+  "color": "purple",
+  "shape": "triangle",
+  "flag": "false",
+  "k": 5,
+  "index": 51,
+  "quantity": 81.2290,
+  "rate": 8.5910
+},
+{
+  "color": "red",
+  "shape": "square",
+  "flag": "false",
+  "k": 6,
+  "index": 64,
+  "quantity": 77.1991,
+  "rate": 9.5310
+},
+{
+  "color": "purple",
+  "shape": "triangle",
+  "flag": "false",
+  "k": 7,
+  "index": 65,
+  "quantity": 80.1405,
+  "rate": 5.8240
+},
+{
+  "color": "yellow",
+  "shape": "circle",
+  "flag": "true",
+  "k": 8,
+  "index": 73,
+  "quantity": 63.9785,
+  "rate": 4.2370
+},
+{
+  "color": "yellow",
+  "shape": "circle",
+  "flag": "true",
+  "k": 9,
+  "index": 87,
+  "quantity": 63.5058,
+  "rate": 8.3350
+},
+{
+  "color": "purple",
+  "shape": "square",
+  "flag": "false",
+  "k": 10,
+  "index": 91,
+  "quantity": 72.3735,
+  "rate": 8.2430
+}
+]
+
+ +
+mlr --c2j --from example.csv
+
+
+[
+{
+  "color": "yellow",
+  "shape": "triangle",
+  "flag": "true",
+  "k": 1,
+  "index": 11,
+  "quantity": 43.6498,
+  "rate": 9.8870
+},
+{
+  "color": "red",
+  "shape": "square",
+  "flag": "true",
+  "k": 2,
+  "index": 15,
+  "quantity": 79.2778,
+  "rate": 0.0130
+},
+{
+  "color": "red",
+  "shape": "circle",
+  "flag": "true",
+  "k": 3,
+  "index": 16,
+  "quantity": 13.8103,
+  "rate": 2.9010
+},
+{
+  "color": "red",
+  "shape": "square",
+  "flag": "false",
+  "k": 4,
+  "index": 48,
+  "quantity": 77.5542,
+  "rate": 7.4670
+},
+{
+  "color": "purple",
+  "shape": "triangle",
+  "flag": "false",
+  "k": 5,
+  "index": 51,
+  "quantity": 81.2290,
+  "rate": 8.5910
+},
+{
+  "color": "red",
+  "shape": "square",
+  "flag": "false",
+  "k": 6,
+  "index": 64,
+  "quantity": 77.1991,
+  "rate": 9.5310
+},
+{
+  "color": "purple",
+  "shape": "triangle",
+  "flag": "false",
+  "k": 7,
+  "index": 65,
+  "quantity": 80.1405,
+  "rate": 5.8240
+},
+{
+  "color": "yellow",
+  "shape": "circle",
+  "flag": "true",
+  "k": 8,
+  "index": 73,
+  "quantity": 63.9785,
+  "rate": 4.2370
+},
+{
+  "color": "yellow",
+  "shape": "circle",
+  "flag": "true",
+  "k": 9,
+  "index": 87,
+  "quantity": 63.5058,
+  "rate": 8.3350
+},
+{
+  "color": "purple",
+  "shape": "square",
+  "flag": "false",
+  "k": 10,
+  "index": 91,
+  "quantity": 72.3735,
+  "rate": 8.2430
+}
+]
+
+ +The second form is equivalent to `mlr --c2j cat example.csv`. Piped stdin works the same way: `cat example.csv | mlr --c2j`. + ## File names up front, including --from Already, we saw that you can put the filename first using `--from`. When you're interacting with your data at the command line, this makes it easier to up-arrow and append to the previous command: diff --git a/docs/src/keystroke-savers.md.in b/docs/src/keystroke-savers.md.in index 648c63fc7..b39a6affc 100644 --- a/docs/src/keystroke-savers.md.in +++ b/docs/src/keystroke-savers.md.in @@ -14,6 +14,20 @@ GENMD-EOF You can get the full list [here](file-formats.md#data-conversion-keystroke-savers). +## Omitting the verb for format conversions + +The verb defaults to [`cat`](reference-verbs.md#cat), so for pure format conversions you can leave the verb off entirely: + +GENMD-RUN-COMMAND +mlr --c2j cat example.csv +GENMD-EOF + +GENMD-RUN-COMMAND +mlr --c2j --from example.csv +GENMD-EOF + +The second form is equivalent to `mlr --c2j cat example.csv`. Piped stdin works the same way: `cat example.csv | mlr --c2j`. + ## File names up front, including --from Already, we saw that you can put the filename first using `--from`. When you're interacting with your data at the command line, this makes it easier to up-arrow and append to the previous command: diff --git a/docs/src/manpage.md b/docs/src/manpage.md index 8a4de9a56..71f87da1b 100644 --- a/docs/src/manpage.md +++ b/docs/src/manpage.md @@ -26,9 +26,13 @@ This is simply a copy of what you should see on running `man mlr` at a command p as CSV and tabular JSON. 1mSYNOPSIS0m - Usage: mlr [flags] {verb} [verb-dependent options ...] {zero or more + Usage: mlr [flags] [verb] [verb-dependent options ...] {zero or more file names} + If no verb is given, "cat" is used, which is handy for format + conversions, e.g. + mlr --c2j < example.csv + If zero file names are provided, standard input is read, e.g. mlr --csv sort -f shape example.csv @@ -3846,5 +3850,5 @@ This is simply a copy of what you should see on running `man mlr` at a command p MIME Type for Comma-Separated Values (CSV) Files, the Miller docsite https://miller.readthedocs.io - 2026-05-16 4mMILLER24m(1) + 2026-05-17 4mMILLER24m(1) diff --git a/docs/src/manpage.txt b/docs/src/manpage.txt index c505c8ee4..e55be814b 100644 --- a/docs/src/manpage.txt +++ b/docs/src/manpage.txt @@ -5,9 +5,13 @@ as CSV and tabular JSON. 1mSYNOPSIS0m - Usage: mlr [flags] {verb} [verb-dependent options ...] {zero or more + Usage: mlr [flags] [verb] [verb-dependent options ...] {zero or more file names} + If no verb is given, "cat" is used, which is handy for format + conversions, e.g. + mlr --c2j < example.csv + If zero file names are provided, standard input is read, e.g. mlr --csv sort -f shape example.csv @@ -3825,4 +3829,4 @@ MIME Type for Comma-Separated Values (CSV) Files, the Miller docsite https://miller.readthedocs.io - 2026-05-16 4mMILLER24m(1) + 2026-05-17 4mMILLER24m(1) diff --git a/docs/src/online-help.md b/docs/src/online-help.md index f112b1e18..9571696d5 100644 --- a/docs/src/online-help.md +++ b/docs/src/online-help.md @@ -26,7 +26,10 @@ The front door is `mlr --help` or its synonym `mlr -h`. This leads you to `mlr h mlr --help
-Usage: mlr [flags] {verb} [verb-dependent options ...] {zero or more file names}
+Usage: mlr [flags] [verb] [verb-dependent options ...] {zero or more file names}
+
+If no verb is given, "cat" is used, which is handy for format conversions, e.g.
+  mlr --c2j < example.csv
 
 If zero file names are provided, standard input is read, e.g.
   mlr --csv sort -f shape example.csv
diff --git a/docs/src/reference-main-overview.md b/docs/src/reference-main-overview.md
index b7e1a97c4..de241cf5f 100644
--- a/docs/src/reference-main-overview.md
+++ b/docs/src/reference-main-overview.md
@@ -66,6 +66,8 @@ See also the [Glossary](glossary.md) for more about terms such as
 
 When you type `mlr {something} myfile.dat`, the `{something}` part is called a **verb**. It specifies how you want to transform your data. Most of the verbs are counterparts of built-in system tools like `cut` and `sort` -- but with file-format awareness, and giving you the ability to refer to fields by name.
 
+The verb is optional: if you omit it, Miller behaves as if you had written [`cat`](reference-verbs.md#cat), which is handy for pure format conversions such as `mlr --c2j < input.csv`.
+
 The verbs `put` and `filter` are special in that they have a rich expression language (domain-specific language, or "DSL"). More information about them can be found on the [Intro to Miller's Programming Language page](miller-programming-language.md); see also the [DSL Reference](reference-dsl.md) for more details.
 
 Here's a comparison of verbs and `put`/`filter` DSL expressions:
diff --git a/docs/src/reference-main-overview.md.in b/docs/src/reference-main-overview.md.in
index 42c3b8f0c..cd36e3583 100644
--- a/docs/src/reference-main-overview.md.in
+++ b/docs/src/reference-main-overview.md.in
@@ -35,6 +35,8 @@ See also the [Glossary](glossary.md) for more about terms such as
 
 When you type `mlr {something} myfile.dat`, the `{something}` part is called a **verb**. It specifies how you want to transform your data. Most of the verbs are counterparts of built-in system tools like `cut` and `sort` -- but with file-format awareness, and giving you the ability to refer to fields by name.
 
+The verb is optional: if you omit it, Miller behaves as if you had written [`cat`](reference-verbs.md#cat), which is handy for pure format conversions such as `mlr --c2j < input.csv`.
+
 The verbs `put` and `filter` are special in that they have a rich expression language (domain-specific language, or "DSL"). More information about them can be found on the [Intro to Miller's Programming Language page](miller-programming-language.md); see also the [DSL Reference](reference-dsl.md) for more details.
 
 Here's a comparison of verbs and `put`/`filter` DSL expressions:
diff --git a/docs/src/reference-verbs.md b/docs/src/reference-verbs.md
index 724f5bcd9..342c9d292 100644
--- a/docs/src/reference-verbs.md
+++ b/docs/src/reference-verbs.md
@@ -274,7 +274,27 @@ Options:
 
 ## cat
 
-Most useful for format conversions (see [File Formats](file-formats.md)) and concatenating multiple same-schema CSV files to have the same header:
+Most useful for format conversions (see [File Formats](file-formats.md)) and concatenating multiple same-schema CSV files to have the same header.
+
+`cat` is also the default verb: if you omit the verb entirely, Miller behaves as if you had written `cat`. This makes pure format conversions more concise:
+
+
+mlr --c2j < data/a.csv
+
+
+[
+{
+  "a": 1,
+  "b": 2,
+  "c": 3
+},
+{
+  "a": 4,
+  "b": 5,
+  "c": 6
+}
+]
+
 mlr cat -h
@@ -1828,7 +1848,7 @@ Same, but showing only unpaired records:
   data/join-right-example.csv
 
-status  idcode
+status  id
 missing 600
 
 id  name
diff --git a/docs/src/reference-verbs.md.in b/docs/src/reference-verbs.md.in
index 5f6f31097..adedd4829 100644
--- a/docs/src/reference-verbs.md.in
+++ b/docs/src/reference-verbs.md.in
@@ -155,7 +155,13 @@ GENMD-EOF
 
 ## cat
 
-Most useful for format conversions (see [File Formats](file-formats.md)) and concatenating multiple same-schema CSV files to have the same header:
+Most useful for format conversions (see [File Formats](file-formats.md)) and concatenating multiple same-schema CSV files to have the same header.
+
+`cat` is also the default verb: if you omit the verb entirely, Miller behaves as if you had written `cat`. This makes pure format conversions more concise:
+
+GENMD-RUN-COMMAND
+mlr --c2j < data/a.csv
+GENMD-EOF
 
 GENMD-RUN-COMMAND
 mlr cat -h
diff --git a/man/manpage.txt b/man/manpage.txt
index c505c8ee4..e55be814b 100644
--- a/man/manpage.txt
+++ b/man/manpage.txt
@@ -5,9 +5,13 @@
        as CSV and tabular JSON.
 
 1mSYNOPSIS0m
-       Usage: mlr [flags] {verb} [verb-dependent options ...] {zero or more
+       Usage: mlr [flags] [verb] [verb-dependent options ...] {zero or more
        file names}
 
+       If no verb is given, "cat" is used, which is handy for format
+       conversions, e.g.
+         mlr --c2j < example.csv
+
        If zero file names are provided, standard input is read, e.g.
          mlr --csv sort -f shape example.csv
 
@@ -3825,4 +3829,4 @@
        MIME Type for Comma-Separated Values (CSV) Files, the Miller docsite
        https://miller.readthedocs.io
 
-                                  2026-05-16                         4mMILLER24m(1)
+                                  2026-05-17                         4mMILLER24m(1)
diff --git a/man/mlr.1 b/man/mlr.1
index 7108bfde8..f1eca6002 100644
--- a/man/mlr.1
+++ b/man/mlr.1
@@ -2,12 +2,12 @@
 .\"     Title: mlr
 .\"    Author: [see the "AUTHOR" section]
 .\" Generator: ./mkman.rb
-.\"      Date: 2026-05-16
+.\"      Date: 2026-05-17
 .\"    Manual: \ \&
 .\"    Source: \ \&
 .\"  Language: English
 .\"
-.TH "MILLER" "1" "2026-05-16" "\ \&" "\ \&"
+.TH "MILLER" "1" "2026-05-17" "\ \&" "\ \&"
 .\" -----------------------------------------------------------------
 .\" * Portability definitions
 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -29,7 +29,10 @@
 Miller \-\- like awk, sed, cut, join, and sort for name-indexed data such as CSV and tabular JSON.
 .SH "SYNOPSIS"
 .sp
-Usage: mlr [flags] {verb} [verb-dependent options ...] {zero or more file names}
+Usage: mlr [flags] [verb] [verb-dependent options ...] {zero or more file names}
+
+If no verb is given, "cat" is used, which is handy for format conversions, e.g.
+  mlr --c2j < example.csv
 
 If zero file names are provided, standard input is read, e.g.
   mlr --csv sort -f shape example.csv
diff --git a/pkg/climain/mlrcli_parse.go b/pkg/climain/mlrcli_parse.go
index 5963fc985..78837b557 100644
--- a/pkg/climain/mlrcli_parse.go
+++ b/pkg/climain/mlrcli_parse.go
@@ -245,9 +245,14 @@ func parseCommandLinePassOne(
 		}
 
 		if len(verbSequences) == 0 {
-			fmt.Fprintf(os.Stderr, "%s: no verb supplied.\n", "mlr")
-			help.MainUsage(os.Stderr)
-			os.Exit(1)
+			// Bare 'mlr' with no flags, no verb, and no files: keep the
+			// friendly usage banner. Anything else (e.g. 'mlr --c2j' on a
+			// piped stdin) defaults to 'cat'.
+			if argc == 1 {
+				help.MainUsage(os.Stderr)
+				os.Exit(1)
+			}
+			verbSequences = append(verbSequences, []string{"cat"})
 		}
 	}
 
diff --git a/pkg/terminals/help/entry.go b/pkg/terminals/help/entry.go
index 31d752e5f..6a9ed0563 100644
--- a/pkg/terminals/help/entry.go
+++ b/pkg/terminals/help/entry.go
@@ -244,7 +244,10 @@ func HelpMain(args []string) int {
 
 func MainUsage(o *os.File) {
 	fmt.Fprintf(o,
-		`Usage: mlr [flags] {verb} [verb-dependent options ...] {zero or more file names}
+		`Usage: mlr [flags] [verb] [verb-dependent options ...] {zero or more file names}
+
+If no verb is given, "cat" is used, which is handy for format conversions, e.g.
+  mlr --c2j < example.csv
 
 If zero file names are provided, standard input is read, e.g.
   mlr --csv sort -f shape example.csv
diff --git a/test/cases/cli-default-verb-cat/0001/cmd b/test/cases/cli-default-verb-cat/0001/cmd
new file mode 100644
index 000000000..728315268
--- /dev/null
+++ b/test/cases/cli-default-verb-cat/0001/cmd
@@ -0,0 +1 @@
+mlr --j2y < ./${CASEDIR}/input
diff --git a/test/cases/cli-default-verb-cat/0001/experr b/test/cases/cli-default-verb-cat/0001/experr
new file mode 100644
index 000000000..e69de29bb
diff --git a/test/cases/cli-default-verb-cat/0001/expout b/test/cases/cli-default-verb-cat/0001/expout
new file mode 100644
index 000000000..7249bd885
--- /dev/null
+++ b/test/cases/cli-default-verb-cat/0001/expout
@@ -0,0 +1,3 @@
+- b: 22
+  c: 33
+  a: 11
diff --git a/test/cases/cli-default-verb-cat/0001/input b/test/cases/cli-default-verb-cat/0001/input
new file mode 100644
index 000000000..5fa6201b6
--- /dev/null
+++ b/test/cases/cli-default-verb-cat/0001/input
@@ -0,0 +1 @@
+{"b":22,"c":33,"a":11}
diff --git a/test/cases/cli-default-verb-cat/0002/cmd b/test/cases/cli-default-verb-cat/0002/cmd
new file mode 100644
index 000000000..8eef97542
--- /dev/null
+++ b/test/cases/cli-default-verb-cat/0002/cmd
@@ -0,0 +1 @@
+mlr --c2p < ./${CASEDIR}/input
diff --git a/test/cases/cli-default-verb-cat/0002/experr b/test/cases/cli-default-verb-cat/0002/experr
new file mode 100644
index 000000000..e69de29bb
diff --git a/test/cases/cli-default-verb-cat/0002/expout b/test/cases/cli-default-verb-cat/0002/expout
new file mode 100644
index 000000000..d0c04ad13
--- /dev/null
+++ b/test/cases/cli-default-verb-cat/0002/expout
@@ -0,0 +1,3 @@
+a b c
+1 2 3
+4 5 6
diff --git a/test/cases/cli-default-verb-cat/0002/input b/test/cases/cli-default-verb-cat/0002/input
new file mode 100644
index 000000000..88700c714
--- /dev/null
+++ b/test/cases/cli-default-verb-cat/0002/input
@@ -0,0 +1,3 @@
+a,b,c
+1,2,3
+4,5,6
diff --git a/test/cases/cli-default-verb-cat/0003/cmd b/test/cases/cli-default-verb-cat/0003/cmd
new file mode 100644
index 000000000..beaf65901
--- /dev/null
+++ b/test/cases/cli-default-verb-cat/0003/cmd
@@ -0,0 +1 @@
+mlr --icsv --ojson --from test/input/example.csv
diff --git a/test/cases/cli-default-verb-cat/0003/experr b/test/cases/cli-default-verb-cat/0003/experr
new file mode 100644
index 000000000..e69de29bb
diff --git a/test/cases/cli-default-verb-cat/0003/expout b/test/cases/cli-default-verb-cat/0003/expout
new file mode 100644
index 000000000..4277e2c14
--- /dev/null
+++ b/test/cases/cli-default-verb-cat/0003/expout
@@ -0,0 +1,92 @@
+[
+{
+  "color": "yellow",
+  "shape": "triangle",
+  "flag": "true",
+  "k": 1,
+  "index": 11,
+  "quantity": 43.64980000,
+  "rate": 9.88700000
+},
+{
+  "color": "red",
+  "shape": "square",
+  "flag": "true",
+  "k": 2,
+  "index": 15,
+  "quantity": 79.27780000,
+  "rate": 0.01300000
+},
+{
+  "color": "red",
+  "shape": "circle",
+  "flag": "true",
+  "k": 3,
+  "index": 16,
+  "quantity": 13.81030000,
+  "rate": 2.90100000
+},
+{
+  "color": "red",
+  "shape": "square",
+  "flag": "false",
+  "k": 4,
+  "index": 48,
+  "quantity": 77.55420000,
+  "rate": 7.46700000
+},
+{
+  "color": "purple",
+  "shape": "triangle",
+  "flag": "false",
+  "k": 5,
+  "index": 51,
+  "quantity": 81.22900000,
+  "rate": 8.59100000
+},
+{
+  "color": "red",
+  "shape": "square",
+  "flag": "false",
+  "k": 6,
+  "index": 64,
+  "quantity": 77.19910000,
+  "rate": 9.53100000
+},
+{
+  "color": "purple",
+  "shape": "triangle",
+  "flag": "false",
+  "k": 7,
+  "index": 65,
+  "quantity": 80.14050000,
+  "rate": 5.82400000
+},
+{
+  "color": "yellow",
+  "shape": "circle",
+  "flag": "true",
+  "k": 8,
+  "index": 73,
+  "quantity": 63.97850000,
+  "rate": 4.23700000
+},
+{
+  "color": "yellow",
+  "shape": "circle",
+  "flag": "true",
+  "k": 9,
+  "index": 87,
+  "quantity": 63.50580000,
+  "rate": 8.33500000
+},
+{
+  "color": "purple",
+  "shape": "square",
+  "flag": "false",
+  "k": 10,
+  "index": 91,
+  "quantity": 72.37350000,
+  "rate": 8.24300000
+}
+]
diff --git a/test/cases/cli-default-verb-cat/0004/cmd b/test/cases/cli-default-verb-cat/0004/cmd
new file mode 100644
index 000000000..4fe92612e
--- /dev/null
+++ b/test/cases/cli-default-verb-cat/0004/cmd
@@ -0,0 +1 @@
+${MLR}
diff --git a/test/cases/cli-default-verb-cat/0004/experr b/test/cases/cli-default-verb-cat/0004/experr
new file mode 100644
index 000000000..a0e8d6ffc
--- /dev/null
+++ b/test/cases/cli-default-verb-cat/0004/experr
@@ -0,0 +1,13 @@
+Usage: mlr [flags] [verb] [verb-dependent options ...] {zero or more file names}
+
+If no verb is given, "cat" is used, which is handy for format conversions, e.g.
+  mlr --c2j < example.csv
+
+If zero file names are provided, standard input is read, e.g.
+  mlr --csv sort -f shape example.csv
+
+Output of one verb may be chained as input to another using "then", e.g.
+  mlr --csv stats1 -a min,mean,max -f quantity then sort -f color example.csv
+
+Please see 'mlr help topics' for more information.
+Please also see https://miller.readthedocs.io
diff --git a/test/cases/cli-default-verb-cat/0004/expout b/test/cases/cli-default-verb-cat/0004/expout
new file mode 100644
index 000000000..e69de29bb
diff --git a/test/cases/cli-default-verb-cat/0004/should-fail b/test/cases/cli-default-verb-cat/0004/should-fail
new file mode 100644
index 000000000..e69de29bb