>_GRCRS — PIPE IT IN. MATCH THE PATTERN. PAINT THE STREAM.
A faithful Rust port of grc (Generic Colouriser 1.13). Run grc <command> and its output comes back colourised. Under the hood, grc joins the command line into a string, scans grc.conf for the first regexp that matches, loads the named colourfile, spawns the command, and pipes its stdout/stderr through grcat — where regexp/colour blocks are applied line by line and emitted as ANSI. Two binaries mirror the original pair: grc the launcher, grcat the colouriser. The regexp engine is fancy-regex; the config grammar, search order, and streaming-colour behaviour match grc 1.13 exactly.
Quickstart
Install from the Homebrew tap or build from source. The formula installs both binaries plus grc.conf and the colourfiles onto the runtime search path.
# install (Homebrew tap — brings binaries + grc.conf + colourfiles) brew install menketechnologies/menketech/grcrs # from source (config lives in the vendor/grc submodule) git clone --recurse-submodules https://github.com/MenkeTechnologies/grcrs cd grcrs && cargo build --release # colourise a command (auto-matched against grc.conf) grc ping example.com grc netstat -tulpn grc df -h # colourise stderr instead of stdout grc -e make # force a specific colourfile, skip grc.conf matching grc -c conf.log tail -f /var/log/system.log
Full install + usage live in the README. The upstream grc man page still describes the config grammar this port implements.
Pipeline architecture
grc is a thin launcher; grcat does the painting. They talk over a pipe. On a match, the command's output flows into grcat <colourfile>, which reads stdin one line at a time and writes coloured text to stdout.
If no regexp in grc.conf matches, or colour is disabled, the command runs uncoloured with inherited stdio and its exit status is propagated. The wrapper ignores SIGINT so Ctrl-C reaches the wrapped command; grc still reaps the child and returns its exit code. With --pty, the command's stdout is connected to a pseudo-terminal so it emits tty-style output even though it is really being piped (experimental).
Config search — how files are found
Both binaries resolve config exactly as grc 1.13 does. grc looks for grc.conf; grcat looks for a colourfile by name. XDG variables default to ~/.config and ~/.local/share when unset. First existing file wins.
| File | Searched in (in order) |
|---|---|
| grc.conf | /etc, /usr/local/etc, /opt/homebrew/etc, $XDG_CONFIG_HOME/grc, ~/.grc |
| colourfiles | (empty prefix), $XDG_DATA_HOME/grc, $XDG_CONFIG_HOME/grc, ~/.grc, /usr/local/share/grc, /usr/share/grc, /opt/homebrew/share/grc |
The colourfile path includes an empty prefix, so an absolute or relative name passed to -c (e.g. -c ./my.conf) is honoured as-is before any search directory is consulted.
Options — the grc launcher
Option scanning stops at the first non-option argument (the command). Short options cluster (-se); --config and --colour take a value inline (--config=name) or as the next argument.
| Option | Argument | Behavior |
|---|---|---|
| -e, --stderr | Redirect stderr through grcat. When set, stdout is not auto-redirected. | |
| -s, --stdout | Redirect stdout, even if -e is also selected. | |
| -c, --config | NAME | Use NAME as the grcat colourfile, skipping grc.conf matching. |
| --colour | on|off|auto | Force colour on, off, or auto (on when stdout is a tty). Default: auto. |
| --pty | Run the command in a pseudo-terminal (experimental). |
grcat takes exactly one argument — the colourfile name — and reads stdin. It is not meant to be called directly (grc pipes into it), but some-command | grcat conf.log works.
Colourfile grammar
A colourfile is a sequence of blocks. A block is a run of keyword=value lines; a blank line, a # comment, or any line whose first character is not an ASCII letter ends the block. Keyword case is folded, and colors / color / colour all normalise to colours.
regexp
The pattern matched against each input line. Python \</\> are translated to literal </> so grc configs behave as authored under fancy-regex.
colours
Comma-separated list, one entry per capture group. Space-joined tokens compose (bold red). Groups beyond the list reuse colour 0.
count
Loop directive: more (default, keep matching), once, stop, block, unblock, previous.
command
Shell command run via sh -c on a match — for side effects (notifications, logging).
skip
yes / 1 / true suppresses the matched line from output entirely.
replace
Replacement string applied to the line. Python \N backrefs are converted to ${N}; literal $ is escaped.
concat
Append the matched line to a file (create-and-append).
trend grcrs
Comma-separated direction:colour pairs (rising, falling, steady) that recolour a numeric group by the sign of its change across the stream. Not in upstream grc.
key grcrs
Capture group whose text keys the per-key last-value map used by trend.
metric grcrs
Capture group whose text is parsed as the number tracked by trend.
Streaming trend colour grcrs extension
The trend keyword colours a numeric capture group by the sign of value − last[key] rather than by which regexp matched, so the colour encodes the value's motion over the stream — a counter climbing, an RTT rising, free memory falling. grcat keeps a per-key last-seen value; each line, the metric group is coloured rising, falling, or steady depending on how it compares to the previous value for the same key. The first observation of a key, and any repeated equal value, count as steady; a group that does not parse as a number keeps its colours entry. This is not part of upstream grc 1.13 — colourfiles without trend are unaffected.
# colour each name's number green when it rises, red when it falls
regexp=(\w+)\s+(\d+)
colours=default,default
trend=rising:green,falling:red,steady:dark
key=1
metric=2
Group 1 (the name) keys the map and group 2 (the number) is recoloured by its trend under that key, so each name's series climbs and falls independently.
Colour tokens
Colour tokens resolve through a static ANSI table, or a "..." quoted literal that is unescaped like a Python string (octal \033, hex \xNN, and the common single-char escapes such as \n, \t, \a).
| Group | Tokens |
|---|---|
| Attributes | none, default, bold, underline, blink, reverse, concealed, dark, italic, rapidblink, strikethrough, beep |
| Foreground | black, red, green, yellow, blue, magenta, cyan, white |
| Background | on_black … on_white |
| Bright (aixterm) | bright_black … bright_white, on_bright_black … on_bright_white (emitted with a standard-code prefix for graceful fallback) |
| Streaming | previous (reuse prior line's colour), unchanged (leave the span untouched) |
Loop directives & streaming colour
The count keyword controls how a pattern iterates within one line, and some directives carry state across lines:
more(default) — after a match, continue scanning the rest of the line for further matches of the same pattern.once— colour the first match, then stop scanning this pattern on this line.stop— colour the match, then stop processing all remaining patterns for this line.block/unblock— enter/leave a mode where every subsequent line is painted in a single block colour untilunblockmatches.previous— resolve to whatevercountthe previous match used.
The previous colour token and the first/last-char bookkeeping let a colour carry from one line into the next, so multi-line constructs (e.g. an indented continuation) inherit the colour of the line that opened them — the same behaviour as grcat.
Examples
# auto-matched against grc.conf grc ping example.com grc ifconfig grc netstat -tulpn grc make # colourise stderr; when -e is set, stdout is not auto-redirected grc -e ./configure # colourise both streams grc -s -e ./build.sh # force a specific colourfile (skip grc.conf matching) grc -c conf.df df -h grc --config=conf.log tail -f /var/log/system.log # control colour explicitly grc --colour=on ls -l # force colour even when piped grc --colour=off netstat # disable colour # experimental: run under a pty so the command emits tty-style output grc --pty top # grcat directly (grc normally does this for you) some-command | grcat conf.log
Bundled configs
The vendor/grc submodule ships 83 colourfiles (conf.ant … conf.yaml) plus a grc.conf mapping commands to them — everything from ping, netstat, ifconfig, df, and du to docker, ip, iptables, mount, and log formats. The Homebrew formula installs grc.conf to $(brew --prefix)/etc and the colourfiles to $(brew --prefix)/share/grc. From source, place them on any directory in the search paths above.
Development & CI
The repository ships a GitHub Actions workflow at .github/workflows/ci.yml that runs cargo fmt --check, cargo clippy --all-targets -D warnings, cargo doc -D warnings, and a build + cargo test matrix on ubuntu-latest and macos-latest, plus a binary smoke test — on every push to main and every pull request. 31 tests back the port: 7 launcher unit tests in src/grcrs.rs (option parsing, regexp translation, config matching), 11 colouriser unit tests in src/grcatrs.rs (colour table, unescape, backref conversion, config parsing), and 13 end-to-end tests in tests/cli.rs that spawn the built binaries and assert on the coloured output. A separate release.yml builds release artifacts.
For source layout, dependency rationale, and the port mapping from grc's Python to Rust, see the engineering report.