From 8bfa7a454e26a5e5579c5975f5574cb8703f14bc Mon Sep 17 00:00:00 2001 From: Mathias Wulff Date: Fri, 23 Oct 2026 23:37:51 +1100 Subject: [PATCH 1/7] Make the README and the help true about --quiet, --debug, --simulate, --sort and more --- README.md | 16 ++++++++-------- src/cli.ts | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 62f6f65c..baadf02d 100644 --- a/README.md +++ b/README.md @@ -233,7 +233,7 @@ Use --js-pipe to access the pipe data in your javascript code. #### Templates -With `--replacement-file` the replacement is the path to a file, and the content of the file goes in. `--trim-file` takes away the line break at the end of the file. +With `--replacement-file` the replacement is the path to a file, and the content of the file goes in. `--trim-file` trims the content, like the line break at the end of the file. ```bash > rexreplace '\{\{footer\}\}' partials/footer.html 'site/*.html' --replacement-file --trim-file @@ -316,7 +316,7 @@ If planned renames clash, the process is halted and no change is applied to any | `-v` | **`--version`** Print rexreplace version (can be given as only argument) [boolean] | | `-V` | **`--verbose`** More chatty output [boolean] | | `-L` | **`--literal`** Literal string search (no regex used when searching) [boolean] | -| `-l` | **`--literal-replacement`** Use the replacement as it is. Without it, `$1`, `$&` and the other `$` signs in the replacement mean the groups and the match, as in javascript. [boolean] | +| `-l` | **`--literal-replacement`** The `$` signs in the replacement have no meaning, so `$1` and `$&` are just text. Without it they mean the groups and the match, as in javascript. The `€` and `§` aliases and escapes like `\n` are part of what is typed, and are still used. [boolean] | | | **`--replacement-file`** The replacement is the path to a file, and the content of the file is used as the replacement. With --js the code gives the path. [boolean] | | `-I` | **`--void-ignore-case`** Void case insensitive search. Applies to the pattern and to all globs. [boolean] | | `-G` | **`--void-global`** Void global search (stop looking after the first match). [boolean] | @@ -348,8 +348,8 @@ If planned renames clash, the process is halted and no change is applied to any | `-A` | **`--void-async`** Handle files in a synchronous flow. Good to limit memory usage when handling large files. [boolean] | | `-B` | **`--void-backup`** Avoid temporary backing up file. Works async (independent of -A flag) and will speed up things but at one point data lives only in memory, and you will lose the content if the process is interrupted. [boolean] | | `-b` | **`--keep-backup`** Keep a backup file of the original content. With --rename the old file is kept. [boolean] | -| `-S` | **`--simulate`** Execute everything during a run except changes to the file system. Names of affected files and content are still printed as in a usual run. [boolean] | -| | **`--sort`** Process the files one at a time, in a sorted order. The order is chosen from 6 values: `natural` (numbers compared as numbers, and case ignored), `ascii` (alphabetic), `modified`, `created`, `size`, or `none` (the order of the input: the files given first, and then the files found by globs). Files that sort the same (like the same size) are ordered by ascii sort. Without --sort the files are processed asynchronously, so the order of the output can not be guaranteed, but the processing is started in natural order. [string] [choices: "natural", "ascii", "modified", "created", "size", "none"] | +| `-S` | **`--simulate`** Execute everything during a run except the changes RexReplace would normally make to the file system. Code from --js still runs, and can change data even with --simulate. [boolean] | +| | **`--sort`** Process the files one at a time, in a sorted order. The order is chosen from 6 values: `natural` (numbers compared as numbers, and case ignored), `ascii` (by char code, so `B` comes before `a`), `modified`, `created`, `size`, or `none` (the order of the input: the files given first, and then the files found by globs). Files that sort the same (like the same size) are ordered by ascii sort. Without --sort the files are processed asynchronously, so the order of the output can not be guaranteed, but the processing is started in natural order. [string] [choices: "natural", "ascii", "modified", "created", "size", "none"] | | | **`--sort-reverse`** Turn the order of the files around. [boolean] | | `-F` | **`--expect-files`** Check that at least one file is found. The exit code is 1 if not. [boolean] | | `-z` | **`--expect-diff`** Check that at least one file is changed. The exit code is 1 if not. [boolean] | @@ -473,7 +473,7 @@ a_b #### The replacement -1. The replacement as typed, with `€` and `§` as above. Or the piped data with `--replacement-pipe` (`-R`), used as it is. +1. The replacement as typed: `€` is `$`, `§` is `\`, and escapes like `\n` and `\t` are read. Or the piped data with `--replacement-pipe` (`-R`), used as it is. 2. `--js` (`-j`) runs it as code, and the value of the code goes on. `--js-pipe` also gives the code the piped data as `pipe`. 3. `--replacement-file`: what came so far is a path, and the content of that file goes on. 4. `--trim-file` trims it. @@ -538,14 +538,14 @@ b.file 3. The tails: the delimiter also goes after the last item, unless `--no-name-delimiter-tail`, `--no-content-delimiter-tail` or `--no-tail`. Piped data that is printed is left as it came in, with nothing after it. ```bash -> rexreplace o x a.file b.file --name-delimiter ', ' --no-name-delimiter-tail +> rexreplace o x a.file b.file --name-delimiter ', ' --no-name-delimiter-tail --sort natural a.file, b.file > rexreplace o x a.file b.file --null-out | xargs -0 ls -l ``` 4. `--nl` ends stdout and stderr with a line break, if they do not end with one. -5. How much: `--quiet` (`-q`) shows only the errors (❌), and no names or warnings (🟡). `--quiet-total` (`-Q`) shows nothing, and `--verbose` (`-V`) tells each step to stderr. An error that stops the run ends with `No further processing.` -6. The status report: `--status` and `--status-compact` (`-c`) print it as the last thing, also with `--quiet-total`. `--debug` (`-d`) prints only the status report, with what went into the engine and what it did. +5. How much: `--quiet` (`-q`) hides the names and the warnings (🟡), but not the errors (❌), and not the content of `--output` or the matches of `--output-match`. `--quiet-total` (`-Q`) shows nothing, and `--verbose` (`-V`) tells each step to stderr. An error that stops the run ends with `No further processing.` +6. The status report: `--status` and `--status-compact` (`-c`) print it as the last thing, also with `--quiet-total`. `--debug` (`-d`) prints the status report, with what went into the engine and what it did, and hides the rest, except the content of `--output` and the matches of `--output-match`. 7. The exit code, see [Exit codes](#exit-codes). ### Other details diff --git a/src/cli.ts b/src/cli.ts index 5b568196..12493f10 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -78,7 +78,7 @@ const yargs = require('yargs') .boolean('l') .describe( 'l', - 'Use the replacement as it is. Without it, `$1`, `$&` and the other `$` signs in the replacement mean the groups and the match, as in javascript.' + 'The `$` signs in the replacement have no meaning, so `$1` and `$&` are just text. Without it they mean the groups and the match, as in javascript. The `€` and `§` aliases and escapes like `\\n` are part of what is typed, and are still used.' ) .alias('l', 'literal-replacement') @@ -261,14 +261,14 @@ const yargs = require('yargs') .boolean('S') .describe( 'S', - 'Execute everything during a run except changes to the file system. Names of affected files and content are still printed as in a usual run.' + 'Execute everything during a run except the changes RexReplace would normally make to the file system. Code from --js still runs, and can change data even with --simulate.' ) .alias('S', 'simulate') .string('sort') .describe( 'sort', - 'Process the files one at a time, in a sorted order. The order is chosen from 6 values: `natural` (numbers compared as numbers, and case ignored), `ascii` (alphabetic), `modified`, `created`, `size`, or `none` (the order of the input: the files given first, and then the files found by globs). Files that sort the same (like the same size) are ordered by ascii sort. Without --sort the files are processed asynchronously, so the order of the output can not be guaranteed, but the processing is started in natural order.' + 'Process the files one at a time, in a sorted order. The order is chosen from 6 values: `natural` (numbers compared as numbers, and case ignored), `ascii` (by char code, so `B` comes before `a`), `modified`, `created`, `size`, or `none` (the order of the input: the files given first, and then the files found by globs). Files that sort the same (like the same size) are ordered by ascii sort. Without --sort the files are processed asynchronously, so the order of the output can not be guaranteed, but the processing is started in natural order.' ) // Any case, and true and yes are natural .coerce('sort', value => { From ff02376f0d0f154c797fc72e16bbca7d72a35674 Mon Sep 17 00:00:00 2001 From: Mathias Wulff Date: Sat, 24 Oct 2026 00:25:52 +1100 Subject: [PATCH 2/7] Make the README and help true about --quiet, --debug and the escapes --- README.md | 8 ++++---- devguide.md | 2 +- src/cli.ts | 5 ++++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index baadf02d..cc78f669 100644 --- a/README.md +++ b/README.md @@ -325,7 +325,7 @@ If planned renames clash, the process is halted and no change is applied to any | `-u` | **`--unicode`** Treat pattern as a sequence of unicode code points. [boolean] | | `-e` | **`--encoding`** Encoding of files/piped data. [default: "utf8"] | | `-E` | **`--engine`** What regex engine to use. `default` is the engine of the runtime: `V8` with Node and `JSC` (JavaScriptCore) with Bun. The engine of the other runtime can not be picked. [choices: "default", "V8", "JSC"] [default: "default"] | -| `-q` | **`--quiet`** Only display errors (no other info) [boolean] | +| `-q` | **`--quiet`** Hide the names of the files and the warnings. Errors are still shown, and so is the content of --output and the matches of --output-match. [boolean] | | `-Q` | **`--quiet-total`** Never display errors or info [boolean] | | `-f` | **`--full-path`** Print the full path of the named files instead of the relative path. [boolean] | | | **`--name-all`** Print the names of all files found by the globs, also the ones where nothing was changed. [boolean] | @@ -405,7 +405,7 @@ Files identified for potential processing are identified by: - Per default `§` is treated as an alias for `\` in the pattern, the replacement and `--exclude-re`. The main reason is for you not to worry about escaping the escape of an escape. Use the `-§` flag if you need to search or replace the actual section char. -- The `€` and `§` aliases apply to pattern, replacement, `--exclude-re` values and delimiters. File globs, `--exclude-glob` and piped data are used exactly as they are, so a file named `price€.txt` is found without any flags. +- The `€` and `§` aliases apply to pattern, replacement, `--exclude-re` values and delimiters. File globs, `--exclude-glob`, the path of `--replacement-file` and piped data are used exactly as they are, so a file named `price€.txt` is found without any flags. - The default delimiter is a magic newline ensuring that there is _at least_ one `\n` after printed file content, matches or names. This means that if the content already ends on a `\n` no additional `\n` is added. This behaviour can not be recreated with other delimiter values. @@ -473,9 +473,9 @@ a_b #### The replacement -1. The replacement as typed: `€` is `$`, `§` is `\`, and escapes like `\n` and `\t` are read. Or the piped data with `--replacement-pipe` (`-R`), used as it is. +1. The replacement as typed: `€` is `$`, `§` is `\`, and escapes like `\n` and `\t` are read. In `--js` code javascript reads the escapes, and the path of `--replacement-file` is used as it is, with no aliases or escapes. Or the piped data with `--replacement-pipe` (`-R`), used as it is. 2. `--js` (`-j`) runs it as code, and the value of the code goes on. `--js-pipe` also gives the code the piped data as `pipe`. -3. `--replacement-file`: what came so far is a path, and the content of that file goes on. +3. `--replacement-file`: what came so far is the path of a file, and the content of the file goes on. 4. `--trim-file` trims it. 5. It goes into the text. `$1` is group 1, `$&` the whole match, and `$$` one `$`, unless the value came from `--js` code, or `--literal-replacement` (`-l`) is given. diff --git a/devguide.md b/devguide.md index b27fc433..4ba7bae8 100644 --- a/devguide.md +++ b/devguide.md @@ -29,7 +29,7 @@ All output goes through `src/output.ts`. Pick the function by who the message is | `chat(msg)` | `--verbose`, also with `--quiet`, but not with `--quiet-total` | The user: what happens to each file. Only ever to stderr. Also kept in `engine.steps` of the status report. | `Open sync: a.md`, `Nothing changed in: a.md` | - What the run did is in the status report: the engine resolves with it, and `--status` prints it. So there is no extra debug output to print along the way. -- `--debug` prints only the status report, with what went into the engine and what it did. +- `--debug` prints the status report, with what went into the engine and what it did, and hides the rest, except the content of `--output` and the matches of `--output-match`. - `warn()`, `error()`, `checkFailed()` and `panic()` add to the status report as they go, also when the quiet flags hide what they print. - The exit code is 0 when the run is done, 1 when a check from `--expect-*` is not met, 2 when a file gave an error, and 3 for a panic or wrong flags. See Exit codes in the README. diff --git a/src/cli.ts b/src/cli.ts index 12493f10..1c4edc43 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -146,7 +146,10 @@ const yargs = require('yargs') .default('E', 'default') .boolean('q') - .describe('q', 'Only display errors (no other info)') + .describe( + 'q', + 'Hide the names of the files and the warnings. Errors are still shown, and so is the content of --output and the matches of --output-match.' + ) .alias('q', 'quiet') .boolean('Q') From 2ef7ec745c780a3d55d38ec494f0708989e4bf9b Mon Sep 17 00:00:00 2001 From: Mathias Wulff Date: Sat, 24 Oct 2026 00:26:35 +1100 Subject: [PATCH 3/7] Tell in the README that only the --js numbers the code names are made --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cc78f669..701ae48c 100644 --- a/README.md +++ b/README.md @@ -366,7 +366,7 @@ If planned renames clash, the process is halted and no change is applied to any | `-g` | **`--glob-pipe`** Each line of the piped data is a path or a glob of the files to work on. They are used together with the ones given as arguments. [boolean] | | | **`--null-in`** The piped paths and globs of --glob-pipe are separated by null bytes, like from `find -print0` or --null-out. [boolean] | | `-R` | **`--replacement-pipe`** Replacement will be piped in. You still need to provide a dummy value (like `_`) as replacement parameter. [boolean] | -| `-j` | **`--replacement-js`**, **`--js`** Treat replacement as javascript source code. The statement from the last expression will become the replacement string. Purposefully implemented the most insecure way possible to remove _any_ incentive to consider running code from an untrusted part. The full match will be available as a javascript variable named $0 while each captured group will be available as $1, $2, $3, ... and so on. At some point, the $ char _will_ give you a headache when used from the command line, so use €0, €1, €2, €3... instead. If the javascript source code references to the full match or a captured group the code will run once per match. Otherwise, it will run once per file. The code has access to the following variables: `r` as an alias for `require` with both expanded to understand a relative path even if it is not starting with `./`, `fs` from node, `path` from node, `globs` from npm, `pipe`: the data piped into the command with --js-pipe (null if no piped data, and `pipe_` is `❌ `), `find`: pattern searched for (the needle), `text`: full text being searched i.e. file content or piped data (the haystack), `bytes`: total size of the haystack in bytes, `size`: human-friendly representation of the total size of the haystack, `time`: String representing the local time when the command was invoked, `time_obj`: date object representing `time`, `now`: alias for `time`, `cwd`: current process working dir, `nl`: a new-line char, `_`: a single space char (for easy string concatenation). The following values defaults to `❌` if haystack does not originate from a file: `file`: contains the full path of the active file being searched (including full filename), `file_rel`: contains `file` relative to current process working dir, `dirpath`: contains the full path without filename of the active file being searched, `dirpath_rel`: contains `dirpath` relative to current process working dir, `filename`: is the full filename of the active file being searched without path, `name`: filename of the active file being searched with no extension, `ext`: extension of the filename including leading dot, `mtime`: ISO inspired representation of the last local modification time of the current file, `ctime`: ISO representation of the local creation time of the current file. `mtime_obj`: date object representing `mtime`, `ctime_obj`: date object representing `ctime`. The numbers: `$n`: the number of the file, `$t`: the number of files, `$i`: the number of the file among the files with a match so far, `$c`: among the files changed so far, `$m`: the number of the match in the file, `$M`: across all files. Each has cousins with zeros in front (`$n0` has 2 digits, `$n00` 3, and so on to 10) and with a space at the end (`$n_`). A named group is a variable with `rr_` in front: `rr_year` for `(?...)`, and `rr_year_` has a space at the end. A named group can not end with `_`. All variables, except from module, date objects, `nl` and `_`, has a corresponding variable name followed by `_` where the content has an extra space at the end (for easy concatenation). [boolean] | +| `-j` | **`--replacement-js`**, **`--js`** Treat replacement as javascript source code. The statement from the last expression will become the replacement string. Purposefully implemented the most insecure way possible to remove _any_ incentive to consider running code from an untrusted part. The full match will be available as a javascript variable named $0 while each captured group will be available as $1, $2, $3, ... and so on. At some point, the $ char _will_ give you a headache when used from the command line, so use €0, €1, €2, €3... instead. If the javascript source code references to the full match or a captured group the code will run once per match. Otherwise, it will run once per file. The code has access to the following variables: `r` as an alias for `require` with both expanded to understand a relative path even if it is not starting with `./`, `fs` from node, `path` from node, `globs` from npm, `pipe`: the data piped into the command with --js-pipe (null if no piped data, and `pipe_` is `❌ `), `find`: pattern searched for (the needle), `text`: full text being searched i.e. file content or piped data (the haystack), `bytes`: total size of the haystack in bytes, `size`: human-friendly representation of the total size of the haystack, `time`: String representing the local time when the command was invoked, `time_obj`: date object representing `time`, `now`: alias for `time`, `cwd`: current process working dir, `nl`: a new-line char, `_`: a single space char (for easy string concatenation). The following values defaults to `❌` if haystack does not originate from a file: `file`: contains the full path of the active file being searched (including full filename), `file_rel`: contains `file` relative to current process working dir, `dirpath`: contains the full path without filename of the active file being searched, `dirpath_rel`: contains `dirpath` relative to current process working dir, `filename`: is the full filename of the active file being searched without path, `name`: filename of the active file being searched with no extension, `ext`: extension of the filename including leading dot, `mtime`: ISO inspired representation of the last local modification time of the current file, `ctime`: ISO representation of the local creation time of the current file. `mtime_obj`: date object representing `mtime`, `ctime_obj`: date object representing `ctime`. The numbers: `$n`: the number of the file, `$t`: the number of files, `$i`: the number of the file among the files with a match so far, `$c`: among the files changed so far, `$m`: the number of the match in the file, `$M`: across all files. Each has cousins with zeros in front (`$n0` has 2 digits, `$n00` 3, and so on to 10) and with a space at the end (`$n_`). A number is only made when the code names it, so a name built when the code runs, like `eval('$' + 'n')`, is not there. A named group is a variable with `rr_` in front: `rr_year` for `(?...)`, and `rr_year_` has a space at the end. A named group can not end with `_`. All variables, except from module, date objects, `nl` and `_`, has a corresponding variable name followed by `_` where the content has an extra space at the end (for easy concatenation). [boolean] | | `-x` | **`--exclude-re`** Files with a path that matches this regular expression will be excluded from processing. Can be provided multiple times. [string] | | `-X` | **`--exclude-glob`** Files found with this glob will be excluded from being processed. Can be provided multiple times and will act as one group of globs, meaning you can use ! notation to negate. [string] | | `-h` | **`--help`** Display help. [boolean] | From 82fb58ce7a3ebf2fdbfae4914df9af71dfaab6eb Mon Sep 17 00:00:00 2001 From: Mathias Wulff Date: Sat, 24 Oct 2026 01:59:08 +1100 Subject: [PATCH 4/7] Shorter README line on which --js numbers are made and when the code runs per match --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 701ae48c..881ceb25 100644 --- a/README.md +++ b/README.md @@ -366,7 +366,7 @@ If planned renames clash, the process is halted and no change is applied to any | `-g` | **`--glob-pipe`** Each line of the piped data is a path or a glob of the files to work on. They are used together with the ones given as arguments. [boolean] | | | **`--null-in`** The piped paths and globs of --glob-pipe are separated by null bytes, like from `find -print0` or --null-out. [boolean] | | `-R` | **`--replacement-pipe`** Replacement will be piped in. You still need to provide a dummy value (like `_`) as replacement parameter. [boolean] | -| `-j` | **`--replacement-js`**, **`--js`** Treat replacement as javascript source code. The statement from the last expression will become the replacement string. Purposefully implemented the most insecure way possible to remove _any_ incentive to consider running code from an untrusted part. The full match will be available as a javascript variable named $0 while each captured group will be available as $1, $2, $3, ... and so on. At some point, the $ char _will_ give you a headache when used from the command line, so use €0, €1, €2, €3... instead. If the javascript source code references to the full match or a captured group the code will run once per match. Otherwise, it will run once per file. The code has access to the following variables: `r` as an alias for `require` with both expanded to understand a relative path even if it is not starting with `./`, `fs` from node, `path` from node, `globs` from npm, `pipe`: the data piped into the command with --js-pipe (null if no piped data, and `pipe_` is `❌ `), `find`: pattern searched for (the needle), `text`: full text being searched i.e. file content or piped data (the haystack), `bytes`: total size of the haystack in bytes, `size`: human-friendly representation of the total size of the haystack, `time`: String representing the local time when the command was invoked, `time_obj`: date object representing `time`, `now`: alias for `time`, `cwd`: current process working dir, `nl`: a new-line char, `_`: a single space char (for easy string concatenation). The following values defaults to `❌` if haystack does not originate from a file: `file`: contains the full path of the active file being searched (including full filename), `file_rel`: contains `file` relative to current process working dir, `dirpath`: contains the full path without filename of the active file being searched, `dirpath_rel`: contains `dirpath` relative to current process working dir, `filename`: is the full filename of the active file being searched without path, `name`: filename of the active file being searched with no extension, `ext`: extension of the filename including leading dot, `mtime`: ISO inspired representation of the last local modification time of the current file, `ctime`: ISO representation of the local creation time of the current file. `mtime_obj`: date object representing `mtime`, `ctime_obj`: date object representing `ctime`. The numbers: `$n`: the number of the file, `$t`: the number of files, `$i`: the number of the file among the files with a match so far, `$c`: among the files changed so far, `$m`: the number of the match in the file, `$M`: across all files. Each has cousins with zeros in front (`$n0` has 2 digits, `$n00` 3, and so on to 10) and with a space at the end (`$n_`). A number is only made when the code names it, so a name built when the code runs, like `eval('$' + 'n')`, is not there. A named group is a variable with `rr_` in front: `rr_year` for `(?...)`, and `rr_year_` has a space at the end. A named group can not end with `_`. All variables, except from module, date objects, `nl` and `_`, has a corresponding variable name followed by `_` where the content has an extra space at the end (for easy concatenation). [boolean] | +| `-j` | **`--replacement-js`**, **`--js`** Treat replacement as javascript source code. The statement from the last expression will become the replacement string. Purposefully implemented the most insecure way possible to remove _any_ incentive to consider running code from an untrusted part. The full match will be available as a javascript variable named $0 while each captured group will be available as $1, $2, $3, ... and so on. At some point, the $ char _will_ give you a headache when used from the command line, so use €0, €1, €2, €3... instead. The code has access to the following variables: `r` as an alias for `require` with both expanded to understand a relative path even if it is not starting with `./`, `fs` from node, `path` from node, `globs` from npm, `pipe`: the data piped into the command with --js-pipe (null if no piped data, and `pipe_` is `❌ `), `find`: pattern searched for (the needle), `text`: full text being searched i.e. file content or piped data (the haystack), `bytes`: total size of the haystack in bytes, `size`: human-friendly representation of the total size of the haystack, `time`: String representing the local time when the command was invoked, `time_obj`: date object representing `time`, `now`: alias for `time`, `cwd`: current process working dir, `nl`: a new-line char, `_`: a single space char (for easy string concatenation). The following values defaults to `❌` if haystack does not originate from a file: `file`: contains the full path of the active file being searched (including full filename), `file_rel`: contains `file` relative to current process working dir, `dirpath`: contains the full path without filename of the active file being searched, `dirpath_rel`: contains `dirpath` relative to current process working dir, `filename`: is the full filename of the active file being searched without path, `name`: filename of the active file being searched with no extension, `ext`: extension of the filename including leading dot, `mtime`: ISO inspired representation of the last local modification time of the current file, `ctime`: ISO representation of the local creation time of the current file. `mtime_obj`: date object representing `mtime`, `ctime_obj`: date object representing `ctime`. The numbers: `$n`: the number of the file, `$t`: the number of files, `$i`: the number of the file among the files with a match so far, `$c`: among the files changed so far, `$m`: the number of the match in the file, `$M`: across all files. Each has cousins with zeros in front (`$n0` has 2 digits, `$n00` 3, and so on to 10) and with a space at the end (`$n_`). For speed, only the numbers the code names are made, and the code runs once per file unless it names the match, a group, `$m` or `$M`. This is found with a simple text search, so it can be fooled, like by `eval('$' + 'n')`. A named group is a variable with `rr_` in front: `rr_year` for `(?...)`, and `rr_year_` has a space at the end. A named group can not end with `_`. All variables, except from module, date objects, `nl` and `_`, has a corresponding variable name followed by `_` where the content has an extra space at the end (for easy concatenation). [boolean] | | `-x` | **`--exclude-re`** Files with a path that matches this regular expression will be excluded from processing. Can be provided multiple times. [string] | | `-X` | **`--exclude-glob`** Files found with this glob will be excluded from being processed. Can be provided multiple times and will act as one group of globs, meaning you can use ! notation to negate. [string] | | `-h` | **`--help`** Display help. [boolean] | From 8b35b03d932453dcae3e492cc0a7e0ba12c20666 Mon Sep 17 00:00:00 2001 From: Mathias Wulff Date: Sat, 24 Oct 2026 02:10:02 +1100 Subject: [PATCH 5/7] Shorter README line on named groups in --js --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 881ceb25..0e0e3764 100644 --- a/README.md +++ b/README.md @@ -366,7 +366,7 @@ If planned renames clash, the process is halted and no change is applied to any | `-g` | **`--glob-pipe`** Each line of the piped data is a path or a glob of the files to work on. They are used together with the ones given as arguments. [boolean] | | | **`--null-in`** The piped paths and globs of --glob-pipe are separated by null bytes, like from `find -print0` or --null-out. [boolean] | | `-R` | **`--replacement-pipe`** Replacement will be piped in. You still need to provide a dummy value (like `_`) as replacement parameter. [boolean] | -| `-j` | **`--replacement-js`**, **`--js`** Treat replacement as javascript source code. The statement from the last expression will become the replacement string. Purposefully implemented the most insecure way possible to remove _any_ incentive to consider running code from an untrusted part. The full match will be available as a javascript variable named $0 while each captured group will be available as $1, $2, $3, ... and so on. At some point, the $ char _will_ give you a headache when used from the command line, so use €0, €1, €2, €3... instead. The code has access to the following variables: `r` as an alias for `require` with both expanded to understand a relative path even if it is not starting with `./`, `fs` from node, `path` from node, `globs` from npm, `pipe`: the data piped into the command with --js-pipe (null if no piped data, and `pipe_` is `❌ `), `find`: pattern searched for (the needle), `text`: full text being searched i.e. file content or piped data (the haystack), `bytes`: total size of the haystack in bytes, `size`: human-friendly representation of the total size of the haystack, `time`: String representing the local time when the command was invoked, `time_obj`: date object representing `time`, `now`: alias for `time`, `cwd`: current process working dir, `nl`: a new-line char, `_`: a single space char (for easy string concatenation). The following values defaults to `❌` if haystack does not originate from a file: `file`: contains the full path of the active file being searched (including full filename), `file_rel`: contains `file` relative to current process working dir, `dirpath`: contains the full path without filename of the active file being searched, `dirpath_rel`: contains `dirpath` relative to current process working dir, `filename`: is the full filename of the active file being searched without path, `name`: filename of the active file being searched with no extension, `ext`: extension of the filename including leading dot, `mtime`: ISO inspired representation of the last local modification time of the current file, `ctime`: ISO representation of the local creation time of the current file. `mtime_obj`: date object representing `mtime`, `ctime_obj`: date object representing `ctime`. The numbers: `$n`: the number of the file, `$t`: the number of files, `$i`: the number of the file among the files with a match so far, `$c`: among the files changed so far, `$m`: the number of the match in the file, `$M`: across all files. Each has cousins with zeros in front (`$n0` has 2 digits, `$n00` 3, and so on to 10) and with a space at the end (`$n_`). For speed, only the numbers the code names are made, and the code runs once per file unless it names the match, a group, `$m` or `$M`. This is found with a simple text search, so it can be fooled, like by `eval('$' + 'n')`. A named group is a variable with `rr_` in front: `rr_year` for `(?...)`, and `rr_year_` has a space at the end. A named group can not end with `_`. All variables, except from module, date objects, `nl` and `_`, has a corresponding variable name followed by `_` where the content has an extra space at the end (for easy concatenation). [boolean] | +| `-j` | **`--replacement-js`**, **`--js`** Treat replacement as javascript source code. The statement from the last expression will become the replacement string. Purposefully implemented the most insecure way possible to remove _any_ incentive to consider running code from an untrusted part. The full match will be available as a javascript variable named $0 while each captured group will be available as $1, $2, $3, ... and so on. At some point, the $ char _will_ give you a headache when used from the command line, so use €0, €1, €2, €3... instead. The code has access to the following variables: `r` as an alias for `require` with both expanded to understand a relative path even if it is not starting with `./`, `fs` from node, `path` from node, `globs` from npm, `pipe`: the data piped into the command with --js-pipe (null if no piped data, and `pipe_` is `❌ `), `find`: pattern searched for (the needle), `text`: full text being searched i.e. file content or piped data (the haystack), `bytes`: total size of the haystack in bytes, `size`: human-friendly representation of the total size of the haystack, `time`: String representing the local time when the command was invoked, `time_obj`: date object representing `time`, `now`: alias for `time`, `cwd`: current process working dir, `nl`: a new-line char, `_`: a single space char (for easy string concatenation). The following values defaults to `❌` if haystack does not originate from a file: `file`: contains the full path of the active file being searched (including full filename), `file_rel`: contains `file` relative to current process working dir, `dirpath`: contains the full path without filename of the active file being searched, `dirpath_rel`: contains `dirpath` relative to current process working dir, `filename`: is the full filename of the active file being searched without path, `name`: filename of the active file being searched with no extension, `ext`: extension of the filename including leading dot, `mtime`: ISO inspired representation of the last local modification time of the current file, `ctime`: ISO representation of the local creation time of the current file. `mtime_obj`: date object representing `mtime`, `ctime_obj`: date object representing `ctime`. The numbers: `$n`: the number of the file, `$t`: the number of files, `$i`: the number of the file among the files with a match so far, `$c`: among the files changed so far, `$m`: the number of the match in the file, `$M`: across all files. Each has cousins with zeros in front (`$n0` has 2 digits, `$n00` 3, and so on to 10) and with a space at the end (`$n_`). For speed, only the numbers the code names are made, and the code runs once per file unless it names the match, a group, `$m` or `$M`. This is found with a simple text search, so it can be fooled, like by `eval('$' + 'n')`. Access a named group via the name prepended with `rr_`. `(?...)` gives you `rr_year`. A named group name can not end with `_`. All variables, except from module, date objects, `nl` and `_`, has a corresponding variable name followed by `_` where the content has an extra space at the end (for easy concatenation). [boolean] | | `-x` | **`--exclude-re`** Files with a path that matches this regular expression will be excluded from processing. Can be provided multiple times. [string] | | `-X` | **`--exclude-glob`** Files found with this glob will be excluded from being processed. Can be provided multiple times and will act as one group of globs, meaning you can use ! notation to negate. [string] | | `-h` | **`--help`** Display help. [boolean] | From bd9fa9284308cb159fdc853b53aae412c268af48 Mon Sep 17 00:00:00 2001 From: Mathias Wulff Date: Sat, 24 Oct 2026 02:43:04 +1100 Subject: [PATCH 6/7] Shorter help and README for --literal-replacement, --quiet, --simulate and --sort --- README.md | 8 ++++---- src/cli.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0e0e3764..5b7120fc 100644 --- a/README.md +++ b/README.md @@ -316,7 +316,7 @@ If planned renames clash, the process is halted and no change is applied to any | `-v` | **`--version`** Print rexreplace version (can be given as only argument) [boolean] | | `-V` | **`--verbose`** More chatty output [boolean] | | `-L` | **`--literal`** Literal string search (no regex used when searching) [boolean] | -| `-l` | **`--literal-replacement`** The `$` signs in the replacement have no meaning, so `$1` and `$&` are just text. Without it they mean the groups and the match, as in javascript. The `€` and `§` aliases and escapes like `\n` are part of what is typed, and are still used. [boolean] | +| `-l` | **`--literal-replacement`** No "smart" values in the replacement, so you can have text like `$1` and `$&` without escaping them. [boolean] | | | **`--replacement-file`** The replacement is the path to a file, and the content of the file is used as the replacement. With --js the code gives the path. [boolean] | | `-I` | **`--void-ignore-case`** Void case insensitive search. Applies to the pattern and to all globs. [boolean] | | `-G` | **`--void-global`** Void global search (stop looking after the first match). [boolean] | @@ -325,7 +325,7 @@ If planned renames clash, the process is halted and no change is applied to any | `-u` | **`--unicode`** Treat pattern as a sequence of unicode code points. [boolean] | | `-e` | **`--encoding`** Encoding of files/piped data. [default: "utf8"] | | `-E` | **`--engine`** What regex engine to use. `default` is the engine of the runtime: `V8` with Node and `JSC` (JavaScriptCore) with Bun. The engine of the other runtime can not be picked. [choices: "default", "V8", "JSC"] [default: "default"] | -| `-q` | **`--quiet`** Hide the names of the files and the warnings. Errors are still shown, and so is the content of --output and the matches of --output-match. [boolean] | +| `-q` | **`--quiet`** Hide file names and warnings. Errors are still shown, and output from flags like --output and --output-match is still shown. [boolean] | | `-Q` | **`--quiet-total`** Never display errors or info [boolean] | | `-f` | **`--full-path`** Print the full path of the named files instead of the relative path. [boolean] | | | **`--name-all`** Print the names of all files found by the globs, also the ones where nothing was changed. [boolean] | @@ -348,8 +348,8 @@ If planned renames clash, the process is halted and no change is applied to any | `-A` | **`--void-async`** Handle files in a synchronous flow. Good to limit memory usage when handling large files. [boolean] | | `-B` | **`--void-backup`** Avoid temporary backing up file. Works async (independent of -A flag) and will speed up things but at one point data lives only in memory, and you will lose the content if the process is interrupted. [boolean] | | `-b` | **`--keep-backup`** Keep a backup file of the original content. With --rename the old file is kept. [boolean] | -| `-S` | **`--simulate`** Execute everything during a run except the changes RexReplace would normally make to the file system. Code from --js still runs, and can change data even with --simulate. [boolean] | -| | **`--sort`** Process the files one at a time, in a sorted order. The order is chosen from 6 values: `natural` (numbers compared as numbers, and case ignored), `ascii` (by char code, so `B` comes before `a`), `modified`, `created`, `size`, or `none` (the order of the input: the files given first, and then the files found by globs). Files that sort the same (like the same size) are ordered by ascii sort. Without --sort the files are processed asynchronously, so the order of the output can not be guaranteed, but the processing is started in natural order. [string] [choices: "natural", "ascii", "modified", "created", "size", "none"] | +| `-S` | **`--simulate`** Execute everything during a run except changes RexReplace would normally make to the file system. Code from --js still runs, and can change data even with --simulate. [boolean] | +| | **`--sort`** Process files sync in a sorted order chosen from 6 values: `natural` (numbers compared as numbers and case insensitive), `ascii` (by char code, so `B` comes before `a`), `modified`, `created`, `size`, or `none` (the order of the input: files first, then files found by globs). Files sorted the same (like the same size) are ordered by ascii sort. Without --sort files are processed asynchronously, so output order is not guaranteed, but the processing starts in natural order. [string] [choices: "natural", "ascii", "modified", "created", "size", "none"] | | | **`--sort-reverse`** Turn the order of the files around. [boolean] | | `-F` | **`--expect-files`** Check that at least one file is found. The exit code is 1 if not. [boolean] | | `-z` | **`--expect-diff`** Check that at least one file is changed. The exit code is 1 if not. [boolean] | diff --git a/src/cli.ts b/src/cli.ts index 1c4edc43..9c186726 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -78,7 +78,7 @@ const yargs = require('yargs') .boolean('l') .describe( 'l', - 'The `$` signs in the replacement have no meaning, so `$1` and `$&` are just text. Without it they mean the groups and the match, as in javascript. The `€` and `§` aliases and escapes like `\\n` are part of what is typed, and are still used.' + 'No "smart" values in the replacement, so you can have text like `$1` and `$&` without escaping them.' ) .alias('l', 'literal-replacement') @@ -148,7 +148,7 @@ const yargs = require('yargs') .boolean('q') .describe( 'q', - 'Hide the names of the files and the warnings. Errors are still shown, and so is the content of --output and the matches of --output-match.' + 'Hide file names and warnings. Errors are still shown, and output from flags like --output and --output-match is still shown.' ) .alias('q', 'quiet') @@ -264,14 +264,14 @@ const yargs = require('yargs') .boolean('S') .describe( 'S', - 'Execute everything during a run except the changes RexReplace would normally make to the file system. Code from --js still runs, and can change data even with --simulate.' + 'Execute everything during a run except changes RexReplace would normally make to the file system. Code from --js still runs, and can change data even with --simulate.' ) .alias('S', 'simulate') .string('sort') .describe( 'sort', - 'Process the files one at a time, in a sorted order. The order is chosen from 6 values: `natural` (numbers compared as numbers, and case ignored), `ascii` (by char code, so `B` comes before `a`), `modified`, `created`, `size`, or `none` (the order of the input: the files given first, and then the files found by globs). Files that sort the same (like the same size) are ordered by ascii sort. Without --sort the files are processed asynchronously, so the order of the output can not be guaranteed, but the processing is started in natural order.' + 'Process files sync in a sorted order chosen from 6 values: `natural` (numbers compared as numbers and case insensitive), `ascii` (by char code, so `B` comes before `a`), `modified`, `created`, `size`, or `none` (the order of the input: files first, then files found by globs). Files sorted the same (like the same size) are ordered by ascii sort. Without --sort files are processed asynchronously, so output order is not guaranteed, but the processing starts in natural order.' ) // Any case, and true and yes are natural .coerce('sort', value => { From 0c66797691761007ac9c87de5be5953b4dc0dd62 Mon Sep 17 00:00:00 2001 From: Mathias Wulff Date: Sat, 24 Oct 2026 03:29:37 +1100 Subject: [PATCH 7/7] The README no longer says that only the --js numbers the code names are made --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5b7120fc..97cae92c 100644 --- a/README.md +++ b/README.md @@ -366,7 +366,7 @@ If planned renames clash, the process is halted and no change is applied to any | `-g` | **`--glob-pipe`** Each line of the piped data is a path or a glob of the files to work on. They are used together with the ones given as arguments. [boolean] | | | **`--null-in`** The piped paths and globs of --glob-pipe are separated by null bytes, like from `find -print0` or --null-out. [boolean] | | `-R` | **`--replacement-pipe`** Replacement will be piped in. You still need to provide a dummy value (like `_`) as replacement parameter. [boolean] | -| `-j` | **`--replacement-js`**, **`--js`** Treat replacement as javascript source code. The statement from the last expression will become the replacement string. Purposefully implemented the most insecure way possible to remove _any_ incentive to consider running code from an untrusted part. The full match will be available as a javascript variable named $0 while each captured group will be available as $1, $2, $3, ... and so on. At some point, the $ char _will_ give you a headache when used from the command line, so use €0, €1, €2, €3... instead. The code has access to the following variables: `r` as an alias for `require` with both expanded to understand a relative path even if it is not starting with `./`, `fs` from node, `path` from node, `globs` from npm, `pipe`: the data piped into the command with --js-pipe (null if no piped data, and `pipe_` is `❌ `), `find`: pattern searched for (the needle), `text`: full text being searched i.e. file content or piped data (the haystack), `bytes`: total size of the haystack in bytes, `size`: human-friendly representation of the total size of the haystack, `time`: String representing the local time when the command was invoked, `time_obj`: date object representing `time`, `now`: alias for `time`, `cwd`: current process working dir, `nl`: a new-line char, `_`: a single space char (for easy string concatenation). The following values defaults to `❌` if haystack does not originate from a file: `file`: contains the full path of the active file being searched (including full filename), `file_rel`: contains `file` relative to current process working dir, `dirpath`: contains the full path without filename of the active file being searched, `dirpath_rel`: contains `dirpath` relative to current process working dir, `filename`: is the full filename of the active file being searched without path, `name`: filename of the active file being searched with no extension, `ext`: extension of the filename including leading dot, `mtime`: ISO inspired representation of the last local modification time of the current file, `ctime`: ISO representation of the local creation time of the current file. `mtime_obj`: date object representing `mtime`, `ctime_obj`: date object representing `ctime`. The numbers: `$n`: the number of the file, `$t`: the number of files, `$i`: the number of the file among the files with a match so far, `$c`: among the files changed so far, `$m`: the number of the match in the file, `$M`: across all files. Each has cousins with zeros in front (`$n0` has 2 digits, `$n00` 3, and so on to 10) and with a space at the end (`$n_`). For speed, only the numbers the code names are made, and the code runs once per file unless it names the match, a group, `$m` or `$M`. This is found with a simple text search, so it can be fooled, like by `eval('$' + 'n')`. Access a named group via the name prepended with `rr_`. `(?...)` gives you `rr_year`. A named group name can not end with `_`. All variables, except from module, date objects, `nl` and `_`, has a corresponding variable name followed by `_` where the content has an extra space at the end (for easy concatenation). [boolean] | +| `-j` | **`--replacement-js`**, **`--js`** Treat replacement as javascript source code. The statement from the last expression will become the replacement string. Purposefully implemented the most insecure way possible to remove _any_ incentive to consider running code from an untrusted part. The full match will be available as a javascript variable named $0 while each captured group will be available as $1, $2, $3, ... and so on. At some point, the $ char _will_ give you a headache when used from the command line, so use €0, €1, €2, €3... instead. The code has access to the following variables: `r` as an alias for `require` with both expanded to understand a relative path even if it is not starting with `./`, `fs` from node, `path` from node, `globs` from npm, `pipe`: the data piped into the command with --js-pipe (null if no piped data, and `pipe_` is `❌ `), `find`: pattern searched for (the needle), `text`: full text being searched i.e. file content or piped data (the haystack), `bytes`: total size of the haystack in bytes, `size`: human-friendly representation of the total size of the haystack, `time`: String representing the local time when the command was invoked, `time_obj`: date object representing `time`, `now`: alias for `time`, `cwd`: current process working dir, `nl`: a new-line char, `_`: a single space char (for easy string concatenation). The following values defaults to `❌` if haystack does not originate from a file: `file`: contains the full path of the active file being searched (including full filename), `file_rel`: contains `file` relative to current process working dir, `dirpath`: contains the full path without filename of the active file being searched, `dirpath_rel`: contains `dirpath` relative to current process working dir, `filename`: is the full filename of the active file being searched without path, `name`: filename of the active file being searched with no extension, `ext`: extension of the filename including leading dot, `mtime`: ISO inspired representation of the last local modification time of the current file, `ctime`: ISO representation of the local creation time of the current file. `mtime_obj`: date object representing `mtime`, `ctime_obj`: date object representing `ctime`. The numbers: `$n`: the number of the file, `$t`: the number of files, `$i`: the number of the file among the files with a match so far, `$c`: among the files changed so far, `$m`: the number of the match in the file, `$M`: across all files. Each has cousins with zeros in front (`$n0` has 2 digits, `$n00` 3, and so on to 10) and with a space at the end (`$n_`). For speed, the code runs once per file unless it names the match, a group, `$m` or `$M`. Access a named group via the name prepended with `rr_`. `(?...)` gives you `rr_year`. A named group name can not end with `_`. All variables, except from module, date objects, `nl` and `_`, has a corresponding variable name followed by `_` where the content has an extra space at the end (for easy concatenation). [boolean] | | `-x` | **`--exclude-re`** Files with a path that matches this regular expression will be excluded from processing. Can be provided multiple times. [string] | | `-X` | **`--exclude-glob`** Files found with this glob will be excluded from being processed. Can be provided multiple times and will act as one group of globs, meaning you can use ! notation to negate. [string] | | `-h` | **`--help`** Display help. [boolean] |