// GRCRS — GENERIC COLOURISER

grcrs v1.13.0 · Two binaries (grc launcher + grcat colouriser) · fancy-regex engine · 83 bundled colourfiles · XDG-aware config search · GPL-2.0-or-later

1,020 production Rust lines · 360 test lines · 31 tests · 2 direct deps (fancy-regex, libc) · faithful port of grc 1.13

Report GitHub Issues
// Color scheme

>_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.

grc ping example.com │ ▼ join argv → "ping example.com" grc.conf ── first matching regexp ──▶ conf.ping │ ▼ spawn command, pipe stdout/stderr grcat conf.ping ── per-line regexp colouring ──▶ tty

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.

FileSearched 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.

OptionArgumentBehavior
-e, --stderr Redirect stderr through grcat. When set, stdout is not auto-redirected.
-s, --stdout Redirect stdout, even if -e is also selected.
-c, --configNAMEUse NAME as the grcat colourfile, skipping grc.conf matching.
--colouron|off|autoForce 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).

GroupTokens
Attributesnone, default, bold, underline, blink, reverse, concealed, dark, italic, rapidblink, strikethrough, beep
Foregroundblack, red, green, yellow, blue, magenta, cyan, white
Backgroundon_blackon_white
Bright (aixterm)bright_blackbright_white, on_bright_blackon_bright_white (emitted with a standard-code prefix for graceful fallback)
Streamingprevious (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 until unblock matches.
  • previous — resolve to whatever count the 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.antconf.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.