Skip to content

Commit 3e429f5

Browse files
authored
Use "\n" in replace for gsub and sub (#2042)
* Fix issue 1805 * Run `make dev`
1 parent 7121954 commit 3e429f5

22 files changed

Lines changed: 107 additions & 15 deletions

File tree

docs/src/manpage.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,6 +1333,8 @@ This is simply a copy of what you should see on running `man mlr` at a command p
13331333
Usage: mlr gsub [options]
13341334
Replaces old string with new string in specified field(s), with regex support
13351335
for the old string and handling multiple matches, like the `gsub` DSL function.
1336+
The replacement string supports C-style backslash escapes such as \n, \t,
1337+
and \x1f. Write \\ to get a literal backslash.
13361338
See also the `sub` and `ssub` verbs.
13371339
Options:
13381340
-f {a,b,c} Field names to convert.
@@ -1980,7 +1982,10 @@ This is simply a copy of what you should see on running `man mlr` at a command p
19801982
1mssub0m
19811983
Usage: mlr ssub [options]
19821984
Replaces old string with new string in specified field(s), without regex support for
1983-
the old string, like the `ssub` DSL function. See also the `gsub` and `sub` verbs.
1985+
the old string, like the `ssub` DSL function.
1986+
Both the search and replacement strings support C-style backslash escapes such
1987+
as \n, \t, and \x1f. Write \\ to get a literal backslash.
1988+
See also the `gsub` and `sub` verbs.
19841989
Options:
19851990
-f {a,b,c} Field names to convert.
19861991
-r {regex} Regular expression for field names to convert.
@@ -2129,6 +2134,8 @@ This is simply a copy of what you should see on running `man mlr` at a command p
21292134
Usage: mlr sub [options]
21302135
Replaces old string with new string in specified field(s), with regex support
21312136
for the old string and not handling multiple matches, like the `sub` DSL function.
2137+
The replacement string supports C-style backslash escapes such as \n, \t,
2138+
and \x1f. Write \\ to get a literal backslash.
21322139
See also the `gsub` and `ssub` verbs.
21332140
Options:
21342141
-f {a,b,c} Field names to convert.
@@ -3823,5 +3830,5 @@ This is simply a copy of what you should see on running `man mlr` at a command p
38233830
MIME Type for Comma-Separated Values (CSV) Files, the Miller docsite
38243831
https://miller.readthedocs.io
38253832

3826-
2026-04-08 4mMILLER24m(1)
3833+
2026-04-19 4mMILLER24m(1)
38273834
</pre>

docs/src/manpage.txt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,6 +1312,8 @@
13121312
Usage: mlr gsub [options]
13131313
Replaces old string with new string in specified field(s), with regex support
13141314
for the old string and handling multiple matches, like the `gsub` DSL function.
1315+
The replacement string supports C-style backslash escapes such as \n, \t,
1316+
and \x1f. Write \\ to get a literal backslash.
13151317
See also the `sub` and `ssub` verbs.
13161318
Options:
13171319
-f {a,b,c} Field names to convert.
@@ -1959,7 +1961,10 @@
19591961
1mssub0m
19601962
Usage: mlr ssub [options]
19611963
Replaces old string with new string in specified field(s), without regex support for
1962-
the old string, like the `ssub` DSL function. See also the `gsub` and `sub` verbs.
1964+
the old string, like the `ssub` DSL function.
1965+
Both the search and replacement strings support C-style backslash escapes such
1966+
as \n, \t, and \x1f. Write \\ to get a literal backslash.
1967+
See also the `gsub` and `sub` verbs.
19631968
Options:
19641969
-f {a,b,c} Field names to convert.
19651970
-r {regex} Regular expression for field names to convert.
@@ -2108,6 +2113,8 @@
21082113
Usage: mlr sub [options]
21092114
Replaces old string with new string in specified field(s), with regex support
21102115
for the old string and not handling multiple matches, like the `sub` DSL function.
2116+
The replacement string supports C-style backslash escapes such as \n, \t,
2117+
and \x1f. Write \\ to get a literal backslash.
21112118
See also the `gsub` and `ssub` verbs.
21122119
Options:
21132120
-f {a,b,c} Field names to convert.
@@ -3802,4 +3809,4 @@
38023809
MIME Type for Comma-Separated Values (CSV) Files, the Miller docsite
38033810
https://miller.readthedocs.io
38043811

3805-
2026-04-08 4mMILLER24m(1)
3812+
2026-04-19 4mMILLER24m(1)

docs/src/reference-verbs.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1461,6 +1461,8 @@ record_count resource
14611461
Usage: mlr gsub [options]
14621462
Replaces old string with new string in specified field(s), with regex support
14631463
for the old string and handling multiple matches, like the `gsub` DSL function.
1464+
The replacement string supports C-style backslash escapes such as \n, \t,
1465+
and \x1f. Write \\ to get a literal backslash.
14641466
See also the `sub` and `ssub` verbs.
14651467
Options:
14661468
-f {a,b,c} Field names to convert.
@@ -3219,7 +3221,10 @@ See also the "tee" DSL function which lets you do more ad-hoc customization.
32193221
<pre class="pre-non-highlight-in-pair">
32203222
Usage: mlr ssub [options]
32213223
Replaces old string with new string in specified field(s), without regex support for
3222-
the old string, like the `ssub` DSL function. See also the `gsub` and `sub` verbs.
3224+
the old string, like the `ssub` DSL function.
3225+
Both the search and replacement strings support C-style backslash escapes such
3226+
as \n, \t, and \x1f. Write \\ to get a literal backslash.
3227+
See also the `gsub` and `sub` verbs.
32233228
Options:
32243229
-f {a,b,c} Field names to convert.
32253230
-r {regex} Regular expression for field names to convert.
@@ -3725,6 +3730,8 @@ $ each 10 uptime | mlr -p step -a delta -f 11
37253730
Usage: mlr sub [options]
37263731
Replaces old string with new string in specified field(s), with regex support
37273732
for the old string and not handling multiple matches, like the `sub` DSL function.
3733+
The replacement string supports C-style backslash escapes such as \n, \t,
3734+
and \x1f. Write \\ to get a literal backslash.
37283735
See also the `gsub` and `ssub` verbs.
37293736
Options:
37303737
-f {a,b,c} Field names to convert.

man/manpage.txt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,6 +1312,8 @@
13121312
Usage: mlr gsub [options]
13131313
Replaces old string with new string in specified field(s), with regex support
13141314
for the old string and handling multiple matches, like the `gsub` DSL function.
1315+
The replacement string supports C-style backslash escapes such as \n, \t,
1316+
and \x1f. Write \\ to get a literal backslash.
13151317
See also the `sub` and `ssub` verbs.
13161318
Options:
13171319
-f {a,b,c} Field names to convert.
@@ -1959,7 +1961,10 @@
19591961
1mssub0m
19601962
Usage: mlr ssub [options]
19611963
Replaces old string with new string in specified field(s), without regex support for
1962-
the old string, like the `ssub` DSL function. See also the `gsub` and `sub` verbs.
1964+
the old string, like the `ssub` DSL function.
1965+
Both the search and replacement strings support C-style backslash escapes such
1966+
as \n, \t, and \x1f. Write \\ to get a literal backslash.
1967+
See also the `gsub` and `sub` verbs.
19631968
Options:
19641969
-f {a,b,c} Field names to convert.
19651970
-r {regex} Regular expression for field names to convert.
@@ -2108,6 +2113,8 @@
21082113
Usage: mlr sub [options]
21092114
Replaces old string with new string in specified field(s), with regex support
21102115
for the old string and not handling multiple matches, like the `sub` DSL function.
2116+
The replacement string supports C-style backslash escapes such as \n, \t,
2117+
and \x1f. Write \\ to get a literal backslash.
21112118
See also the `gsub` and `ssub` verbs.
21122119
Options:
21132120
-f {a,b,c} Field names to convert.
@@ -3802,4 +3809,4 @@
38023809
MIME Type for Comma-Separated Values (CSV) Files, the Miller docsite
38033810
https://miller.readthedocs.io
38043811

3805-
2026-04-08 4mMILLER24m(1)
3812+
2026-04-19 4mMILLER24m(1)

man/mlr.1

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
.\" Title: mlr
33
.\" Author: [see the "AUTHOR" section]
44
.\" Generator: ./mkman.rb
5-
.\" Date: 2026-04-08
5+
.\" Date: 2026-04-19
66
.\" Manual: \ \&
77
.\" Source: \ \&
88
.\" Language: English
99
.\"
10-
.TH "MILLER" "1" "2026-04-08" "\ \&" "\ \&"
10+
.TH "MILLER" "1" "2026-04-19" "\ \&" "\ \&"
1111
.\" -----------------------------------------------------------------
1212
.\" * Portability definitions
1313
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1631,6 +1631,8 @@ Options:
16311631
Usage: mlr gsub [options]
16321632
Replaces old string with new string in specified field(s), with regex support
16331633
for the old string and handling multiple matches, like the `gsub` DSL function.
1634+
The replacement string supports C-style backslash escapes such as \en, \et,
1635+
and \ex1f. Write \e\e to get a literal backslash.
16341636
See also the `sub` and `ssub` verbs.
16351637
Options:
16361638
-f {a,b,c} Field names to convert.
@@ -2464,7 +2466,10 @@ See also the "tee" DSL function which lets you do more ad-hoc customization.
24642466
.nf
24652467
Usage: mlr ssub [options]
24662468
Replaces old string with new string in specified field(s), without regex support for
2467-
the old string, like the `ssub` DSL function. See also the `gsub` and `sub` verbs.
2469+
the old string, like the `ssub` DSL function.
2470+
Both the search and replacement strings support C-style backslash escapes such
2471+
as \en, \et, and \ex1f. Write \e\e to get a literal backslash.
2472+
See also the `gsub` and `sub` verbs.
24682473
Options:
24692474
-f {a,b,c} Field names to convert.
24702475
-r {regex} Regular expression for field names to convert.
@@ -2637,6 +2642,8 @@ for more information on EWMA.
26372642
Usage: mlr sub [options]
26382643
Replaces old string with new string in specified field(s), with regex support
26392644
for the old string and not handling multiple matches, like the `sub` DSL function.
2645+
The replacement string supports C-style backslash escapes such as \en, \et,
2646+
and \ex1f. Write \e\e to get a literal backslash.
26402647
See also the `gsub` and `ssub` verbs.
26412648
Options:
26422649
-f {a,b,c} Field names to convert.

pkg/transformers/subs.go

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ func transformerSubUsage(
4444
fmt.Fprintf(o, "Usage: %s %s [options]\n", "mlr", verbNameSub)
4545
fmt.Fprintf(o, "Replaces old string with new string in specified field(s), with regex support\n")
4646
fmt.Fprintf(o, "for the old string and not handling multiple matches, like the `sub` DSL function.\n")
47+
fmt.Fprintf(o, "The replacement string supports C-style backslash escapes such as \\n, \\t,\n")
48+
fmt.Fprintf(o, "and \\x1f. Write \\\\ to get a literal backslash.\n")
4749
fmt.Fprintf(o, "See also the `gsub` and `ssub` verbs.\n")
4850
fmt.Fprintf(o, "Options:\n")
4951
fmt.Fprintf(o, "-f {a,b,c} Field names to convert.\n")
@@ -58,6 +60,8 @@ func transformerGsubUsage(
5860
fmt.Fprintf(o, "Usage: %s %s [options]\n", "mlr", verbNameGsub)
5961
fmt.Fprintf(o, "Replaces old string with new string in specified field(s), with regex support\n")
6062
fmt.Fprintf(o, "for the old string and handling multiple matches, like the `gsub` DSL function.\n")
63+
fmt.Fprintf(o, "The replacement string supports C-style backslash escapes such as \\n, \\t,\n")
64+
fmt.Fprintf(o, "and \\x1f. Write \\\\ to get a literal backslash.\n")
6165
fmt.Fprintf(o, "See also the `sub` and `ssub` verbs.\n")
6266
fmt.Fprintf(o, "Options:\n")
6367
fmt.Fprintf(o, "-f {a,b,c} Field names to convert.\n")
@@ -71,7 +75,10 @@ func transformerSsubUsage(
7175
) {
7276
fmt.Fprintf(o, "Usage: %s %s [options]\n", "mlr", verbNameSsub)
7377
fmt.Fprintf(o, "Replaces old string with new string in specified field(s), without regex support for\n")
74-
fmt.Fprintf(o, "the old string, like the `ssub` DSL function. See also the `gsub` and `sub` verbs.\n")
78+
fmt.Fprintf(o, "the old string, like the `ssub` DSL function.\n")
79+
fmt.Fprintf(o, "Both the search and replacement strings support C-style backslash escapes such\n")
80+
fmt.Fprintf(o, "as \\n, \\t, and \\x1f. Write \\\\ to get a literal backslash.\n")
81+
fmt.Fprintf(o, "See also the `gsub` and `sub` verbs.\n")
7582
fmt.Fprintf(o, "Options:\n")
7683
fmt.Fprintf(o, "-f {a,b,c} Field names to convert.\n")
7784
fmt.Fprintf(o, "-r {regex} Regular expression for field names to convert.\n")
@@ -98,7 +105,7 @@ func transformerSubParseCLI(
98105
opts *cli.TOptions,
99106
doConstruct bool, // false for first pass of CLI-parse, true for second pass
100107
) (RecordTransformer, error) {
101-
return transformerSubsParseCLI(pargi, argc, args, opts, doConstruct, transformerSubUsage, NewTransformerSub)
108+
return transformerSubsParseCLI(pargi, argc, args, opts, doConstruct, transformerSubUsage, NewTransformerSub, false)
102109
}
103110

104111
func transformerGsubParseCLI(
@@ -108,7 +115,7 @@ func transformerGsubParseCLI(
108115
opts *cli.TOptions,
109116
doConstruct bool, // false for first pass of CLI-parse, true for second pass
110117
) (RecordTransformer, error) {
111-
return transformerSubsParseCLI(pargi, argc, args, opts, doConstruct, transformerGsubUsage, NewTransformerGsub)
118+
return transformerSubsParseCLI(pargi, argc, args, opts, doConstruct, transformerGsubUsage, NewTransformerGsub, false)
112119
}
113120

114121
func transformerSsubParseCLI(
@@ -118,10 +125,12 @@ func transformerSsubParseCLI(
118125
opts *cli.TOptions,
119126
doConstruct bool, // false for first pass of CLI-parse, true for second pass
120127
) (RecordTransformer, error) {
121-
return transformerSubsParseCLI(pargi, argc, args, opts, doConstruct, transformerSsubUsage, NewTransformerSsub)
128+
return transformerSubsParseCLI(pargi, argc, args, opts, doConstruct, transformerSsubUsage, NewTransformerSsub, true)
122129
}
123130

124131
// transformerSubsParseCLI is a shared CLI-parser for the sub, gsub, and ssub verbs.
132+
// When unbackslashOldText is true (ssub only), the search string is also unescaped;
133+
// for sub/gsub the search string is a regex and Go's regexp engine handles \n/\t/etc.
125134
func transformerSubsParseCLI(
126135
pargi *int,
127136
argc int,
@@ -130,6 +139,7 @@ func transformerSubsParseCLI(
130139
doConstruct bool, // false for first pass of CLI-parse, true for second pass
131140
usageFunc TransformerUsageFunc,
132141
constructorFunc subConstructorFunc,
142+
unbackslashOldText bool,
133143
) (RecordTransformer, error) {
134144

135145
// Skip the verb name from the current spot in the mlr command line
@@ -192,6 +202,18 @@ func transformerSubsParseCLI(
192202
oldText = args[argi]
193203
newText = args[argi+1]
194204

205+
// Interpret C-style backslash escapes ("\n", "\t", "\x1f", etc.) in the
206+
// replacement string the same way the DSL string-literal parser does, so
207+
// that e.g. `mlr sub -a r "\n"` matches `sub($x, "r", "\n")` in the DSL.
208+
// For sub/gsub the search string is a regex and Go's regexp engine already
209+
// handles \n/\r/\t inside patterns; pre-unescaping would corrupt user-
210+
// supplied regex metachars like \d or \s. For ssub the search string is a
211+
// literal, so we unescape it too.
212+
newText = lib.UnbackslashStringLiteral(newText)
213+
if unbackslashOldText {
214+
oldText = lib.UnbackslashStringLiteral(oldText)
215+
}
216+
195217
argi += 2
196218

197219
*pargi = argi

test/cases/cli-help/0001/expout

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,8 @@ gsub
389389
Usage: mlr gsub [options]
390390
Replaces old string with new string in specified field(s), with regex support
391391
for the old string and handling multiple matches, like the `gsub` DSL function.
392+
The replacement string supports C-style backslash escapes such as \n, \t,
393+
and \x1f. Write \\ to get a literal backslash.
392394
See also the `sub` and `ssub` verbs.
393395
Options:
394396
-f {a,b,c} Field names to convert.
@@ -1067,7 +1069,10 @@ See also the "tee" DSL function which lets you do more ad-hoc customization.
10671069
ssub
10681070
Usage: mlr ssub [options]
10691071
Replaces old string with new string in specified field(s), without regex support for
1070-
the old string, like the `ssub` DSL function. See also the `gsub` and `sub` verbs.
1072+
the old string, like the `ssub` DSL function.
1073+
Both the search and replacement strings support C-style backslash escapes such
1074+
as \n, \t, and \x1f. Write \\ to get a literal backslash.
1075+
See also the `gsub` and `sub` verbs.
10711076
Options:
10721077
-f {a,b,c} Field names to convert.
10731078
-r {regex} Regular expression for field names to convert.
@@ -1220,6 +1225,8 @@ sub
12201225
Usage: mlr sub [options]
12211226
Replaces old string with new string in specified field(s), with regex support
12221227
for the old string and not handling multiple matches, like the `sub` DSL function.
1228+
The replacement string supports C-style backslash escapes such as \n, \t,
1229+
and \x1f. Write \\ to get a literal backslash.
12231230
See also the `gsub` and `ssub` verbs.
12241231
Options:
12251232
-f {a,b,c} Field names to convert.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
printf 'a=lorem\n' | mlr --ojson gsub -a '[lm]' '\t'

test/cases/verb-sub-gsub-ssub/non-windows/gsub-escape-tab/experr

Whitespace-only changes.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[
2+
{
3+
"a": "\tore\t"
4+
}
5+
]

0 commit comments

Comments
 (0)