rkg
record + knit + grid
One-liner DSL for shell-gei, record processing, and grid-based text transforms.
Description
rkg is a one-liner oriented record/grid processor for text reshaping work.
It combines record-style operations for delimited text with grid-style operations for line-based patterns, so you can select, replace, reshape, transpose, rotate, and otherwise transform structured text from a compact command-line syntax.
For shell-friendly one-liners, the DSL should prefer a small punctuation set built
around ., :, ,, ;, and = so that common expressions stay readable in
bash/zsh without leaning too heavily on " or ().
Features
r./rec.for record modeg./grid.for grid mode- method chaining with
. - pipeline chaining with
| - statement reset with
; - AWK-like separators:
fs,rs,ofs,ors -F/--field-separatorfor AWK-like initial field separator override-R/--record-separator,-O/--output-field-separator,-N/--output-record-separatorfor initial separator overrides- field selection, replace, explode, implode, groupby, reshape, flatten
- transpose, rotate, and ray/pattern mark operations for grid input
Install
Cargo Install
Build From Source
Usage
Command
Quick Start
|
Shorthand:
|
Existing commands:
|
DSL Shape
r.fs(",").x(2,";").g(1,s(2)).ofs(",");
g.t().rt("r").m(p("K"),"orth","*")
Shorthand forms are also supported for shell-friendly one-liners:
mode.method:arg1,arg2.setting=value;mode.method:arg
mode.method | mode.method
g.t.rt:r
|pipes the previous stage output into the next stage when the right side starts withr./g.method(...)is the classic call formmethod:arg1,arg2is shorthand formethod(arg1,arg2)method=valueis shorthand for single-argument config-style calls likeofs("|")- bare
methodis shorthand for zero-argument calls liket() ;resets evaluation to the original stdin for the next statement group- only the last statement is printed by default
--print-allprints all statement results separated by----Fsets the initial record-modefsbefore the DSL runs, and accepts regex patterns-R,-O, and-Nset initialrs,ofs, andorsbefore the DSL runs- if
EXPRis omitted,rkgdefaults to record-mode passthrough with the initial CLI separators applied - when the shell would treat a character specially, quote the whole DSL as one argument
Quick examples
Record functions
-F re / --field-separator re
Sets the initial record-mode field separator from the CLI before any DSL method runs.
Input:
A,10;tokyo
B:20;osaka
Command:
|
Output:
A|10|tokyo
B|20|osaka
Shorthand:
|
Existing commands:
|
Shorthand pipeline chaining
Passes shorthand output from one stage directly into the next stage with |.
Input:
A 10
B 20
Command:
|
Existing commands:
|
Output:
AB
--
12
00
fs(re)
Splits each record with the given regex instead of the default whitespace separator.
Input:
A,10
B,20
Command:
|
Shorthand:
|
Option only:
|
Existing commands:
|
Output:
A 10
B 20
rs(sep)
Treats the given separator as the boundary between input records.
Input:
A 10|B 20|
Command:
|
Shorthand:
|
Option only:
|
Existing commands:
|
Output:
A 10
B 20
Reference
Record functions
fs(re)input field separator regex, AWK-like (\s+by default)rs(sep)input record separator (\nby default)ofs(sep)output field separator (by default)ors(sep)output record separator (\nby default)p(...)/select(...)select fields, e.g.p(1,"3:")sb(re, rep)/replace(re, rep)regex replace per celln(start_or_AZ)/enum(...)prepend numbering orA-Zcycle labelsx(col, sep)/explode(...)split one field into multiple rowsi(key_col, val_col, join_sep?)/implode(...)collapse rows by keyg(key_col, agg...)/groupby(...)aggregate by keysh(mode, ...)/reshape(...)where mode isw2lorl2wf(template?)/flatten(...)flatten records; optional template like"{name}:{age}"
Aggregators
s(col)/sum(col)c()/count()mn(col)/min(col)mx(col)/max(col)a(col)/avg(col)med(col)/median(col)
Grid functions
fs(sep)optional cell separator; default is character gridrs(sep)/ofs(sep)/ors(sep)get(x, y)/get(p(...))returns a 1-cell grid from a 1-based coordinate or picked pointset(x, y, value)/set(p(...), value)overwrites one cell at a 1-based coordinate or picked pointline(origin, dir, values..., wrap(mode)?, skip(n)?)/ln(...)writes values along a direction or fill-mode from a coordinate or picked pointrev(mode, pad(value)?)/rv(...)reverses the grid horizontally, vertically, or both;pad(...)makes ragged rows rectangular firstt()/transpose()rt("r"|"l"|"180")/rotate(...)m(from, ray, put)marks along a ray (orth,diag,alldir,8);frommay be a literal orpick(value[, n])/p(value[, n])m(origin, "line", dir, values..., wrap(mode)?, skip(n)?)can reuse mark as a line-placement mode from one or more originsm(from, through_re, to, put)8-direction pattern mark, useful for reversi-like scans
Shorthand syntax
r.p:1,3.ofs=|is equivalent tor.p(1,3).ofs("|")r.g:1,s:2is equivalent tor.g(1,s(2))g.get:3,2is equivalent tog.get(3,2)g.set:3,2,Xis equivalent tog.set(3,2,"X")g.rv:his equivalent tog.rev("h")g.rv:h,pad:"."is equivalent tog.rev("h",pad("."))g.ln:2,2,r,A,B,Cis equivalent tog.line(2,2,"r","A","B","C")g.ln:4,1,r,A,B,C,D,wrap:rowis equivalent tog.line(4,1,"r","A","B","C","D",wrap("row"))g.ln:1,1,fur,A,B,C,D,E,F,G,H,I,skip:1is equivalent tog.line(1,1,"fur","A","B","C","D","E","F","G","H","I",skip(1))g.m:p("K"),line,r,A,Bis equivalent tog.m(p("K"),"line","r","A","B")g.m:p("K",2),"diag","*"is equivalent tog.m(p("K",2),"diag","*")- shorthand is most useful for simple one-liners; regular
()calls remain available for anything that needs clearer quoting - the
Existing commandssnippets below are example-specific equivalents built from common shell tools, not drop-in general replacements for the full DSL
Examples
Record functions
ofs(sep) / output field separator
Changes the separator used when fields are joined for output.
Input:
A 10
B 20
Command:
|
Shorthand:
|
Option only:
|
Existing commands:
|
Output:
A,10
B,20
ors(sep) / output record separator
Changes the separator used when output records are joined together.
Input:
A 10
B 20
Command:
|
Shorthand:
|
Option only:
|
Existing commands:
|
Output:
A 10|B 20|
p(...) / select(...)
Keeps only the requested fields and removes the rest.
Input:
A 10 tokyo
B 20 osaka
Command:
|
Shorthand:
|
Existing commands:
|
Output:
A tokyo
B osaka
sb(re, rep) / replace(re, rep)
Replaces text matching the regex in every cell.
Input:
A-10
B-20
Command:
|
Shorthand:
|
Existing commands:
|
Output:
A-XX
B-XX
n(start_or_AZ) / enum(...)
Adds a numeric counter column to the front of each row.
Input:
A 10
B 20
Command:
|
Shorthand:
|
Existing commands:
|
Output:
1 A 10
2 B 20
Uses alphabet labels instead of numbers and prepends them as a new first column.
Input:
A 10
B 20
C 30
Command:
|
Shorthand:
|
Existing commands:
|
Output:
A A 10
B B 20
C C 30
x(col, sep) / explode(...)
Splits one field into multiple rows while keeping the other columns as-is.
Input:
A,10;20;30
B,7;8
Command:
|
Shorthand:
|
Existing commands:
|
Output:
A,10
A,20
A,30
B,7
B,8
i(key_col, val_col, join_sep?) / implode(...)
Merges rows with the same key by joining one value column into a single field.
Input:
A 10
A 20
A 30
B 7
B 8
B 9
C 100
C 200
Command:
|
Shorthand:
|
Existing commands:
|
Output:
A 10,20,30
B 7,8,9
C 100,200
g(key_col, agg...) / groupby(...)
Groups rows by the key column and emits aggregate results per group.
Input:
A,10;20;30
B,7;8
Command:
|
Shorthand:
|
Existing commands:
|
Output:
A,60
B,15
sh("w2l", ...) / reshape(...)
Turns wide columns into repeated long-form rows.
Input:
name math eng
A 80 90
B 70 85
Command:
|
Shorthand:
|
Existing commands:
|
Output:
A math 80
A eng 90
B math 70
B eng 85
sh("l2w", ...) / reshape(...)
Turns repeated long-form rows back into a wide table.
Input:
A math 80
A eng 90
B math 70
B eng 85
Command:
|
Shorthand:
|
Existing commands:
|
|
Output:
key eng math
A 90 80
B 85 70
f(template?) / flatten(...)
Renders each data row as a single string using the header names in the template.
Input:
name age
alice 20
bob 30
carol 25
dave 41
Command:
|
Shorthand:
|
Existing commands:
|
Output:
alice:20
bob:30
carol:25
dave:41
Aggregators
s(col) / sum(col)
Sums the numeric values in the target column for each group.
Input:
A 10
A 20
B 7
Command:
|
Shorthand:
|
Existing commands:
|
|
# or, with datamash:
|
Output:
A 30
B 7
c() / count()
Counts how many rows belong to each group.
Input:
A 10
A 20
B 7
Command:
|
Shorthand:
|
Existing commands:
|
| |
# or, with datamash:
|
Output:
A 2
B 1
mn(col) / min(col)
Takes the smallest numeric value in the target column for each group.
Input:
A 10
A 20
A 15
B 7
B 12
C 3
C 9
Command:
|
Shorthand:
|
Existing commands:
|
|
# or, with datamash:
|
Output:
A 10
B 7
C 3
mx(col) / max(col)
Takes the largest numeric value in the target column for each group.
Input:
A 10
A 20
A 15
B 7
B 12
C 3
C 9
Command:
|
Shorthand:
|
Existing commands:
|
|
# or, with datamash:
|
Output:
A 20
B 12
C 9
a(col) / avg(col)
Computes the average numeric value in the target column for each group.
Input:
A 10
A 20
A 15
B 7
B 12
C 3
C 9
Command:
|
Shorthand:
|
Existing commands:
|
|
# or, with datamash:
|
Output:
A 15
B 9.5
C 6
med(col) / median(col)
Computes the median numeric value in the target column for each group.
Input:
A 10
A 20
A 15
B 7
B 12
C 3
C 9
Command:
|
Shorthand:
|
Existing commands:
|
|
# or, with datamash:
|
Output:
A 15
B 9.5
C 6
Grid functions
fs(sep) / cell separator
Treats each input row as separator-delimited cells instead of a character grid.
Input:
a,b,c
d,e,f
Command:
|
Shorthand:
|
Existing commands:
|
Output:
a|b|c
d|e|f
rs(sep) / record separator
Treats the given separator as the boundary between grid rows.
Input:
abc|def|ghi|
Command:
|
Shorthand:
|
Option only:
|
Existing commands:
|
Output:
abc
def
ghi
ofs(sep) / output field separator
Changes the separator used when cells are joined for each output row.
Input:
abc
def
Command:
|
Shorthand:
|
Option only:
|
Existing commands:
|
Output:
a|b|c
d|e|f
ors(sep) / output record separator
Changes the separator used when output rows are joined together.
Input:
abc
def
Command:
|
Shorthand:
|
Option only:
|
Existing commands:
|
Output:
abc---
def---
t() / transpose()
Swaps rows and columns in the grid.
Input:
abc
def
ghi
Command:
|
Shorthand:
|
Existing commands:
|
Output:
adg
beh
cfi
rt("r"|"l"|"180") / rotate(...)
Rotates the grid 90 degrees clockwise.
Input:
abc
def
ghi
Command:
|
Shorthand:
|
Existing commands:
|
Output:
gda
heb
ifc
Rotates the grid by 180 degrees.
Input:
abc
def
ghi
Command:
|
Shorthand:
|
Existing commands:
|
Output:
ihg
fed
cba
m(from, ray, put) / mark(from, ray, put)
Marks all reachable cells along the specified ray directions from the source cell.
from can be a literal cell value or pick(value[, n]) / p(value[, n]), which selects the nth matching cell in top-to-bottom, left-to-right order.
Input:
.......
.......
...K...
.......
.......
Command:
|
Nth match:
|
Existing commands:
|
Output:
...*...
...*...
***K***
...*...
...*...
Nth match output:
*...*
.*.*.
..K..
.*.*.
*...*
m(from, through_re, to, put) / mark(from, through_re, to, put)
Marks only the matching middle cells when they are sandwiched between from and to.
Input:
.......
.......
.XOOOX.
.......
.......
Command:
|
Shorthand:
|
Existing commands:
|
Output:
.......
.......
.X***X.
.......
.......
rev(mode, pad(value)?) / rv(mode, pad(value)?)
Reverses the grid horizontally, vertically, or both.
Use mode as h, v, or hv.
If rows have different widths, pad(value) can make them rectangular before reversing.
Input:
abc
def
ghi
Command:
|
Shorthand:
|
Output:
cba
fed
ihg
Pad example:
|
Shorthand:
|
Output:
.ba
edc
line(origin, dir, values..., wrap(mode)?, skip(n)?) / ln(origin, dir, values..., wrap(mode)?, skip(n)?)
Writes values along a direction or fill-mode from a coordinate or picked point.
One-way directions are right / r, left / l, up / u, down / d, ur, ul, dr, dl.
Centered directions are horiz / h, vert / v, diag_dr / xr, and diag_dl / xl, and they require an odd number of values so the middle value lands on the origin.
Fill modes are fill_ur / fur and fill_ul / ful.
wrap(mode) is optional and only works for one-way directions:
r/lwithwrap("row")u/dwithwrap("col")dr/ulwithwrap("diag_dr")dl/urwithwrap("diag_dl")
skip(n) is optional and only works for fill modes. It skips the first n cells in the fill traversal before writing values.
Input:
.....
.....
.....
Command:
|
Shorthand:
|
Wrapped shorthand:
|
Output:
.....
.ABC.
.....
Diagonal example:
|
Output:
.....
.C...
..O..
...D.
....E
Diagonal shorthand:
|
Diagonal wrap example:
|
Output:
.D...
.....
..A..
...B.
....C
Top-edge diagonal example:
Coordinates are 1-based, so the top-left corner is (1,1).
|
Output:
H....
.E...
..L..
...L.
....O
Second-row diagonal example:
|
Output:
.....
S....
.L...
..A..
...N.
Fill-mode example:
|
Existing commands:
|
Output:
.BEI.
ADH..
CG...
F....
.....
Shifted fill-mode example:
|
Output:
..CG.
.BF..
AE...
D....
.....
You can also route the same behavior through mark mode:
|
Multiple byte example:
|
|
|
Output:
ンこボそ $
ウととルコ $
コ子ビン $
のィチ $
デて $
し $
$
Multiple statements
Runs each statement against the original stdin, so later statements do not receive earlier output.
Input:
A 10,20
B 7,8
Command:
|
Shorthand:
|
Existing commands:
{ | ; ; | ; }
Output:
A 30
B 15
---
1 A 10,20
2 B 7,8
Notes
fsis treated as a regex for record mode, similar to AWKFS- CSV quoting is not implemented; this prototype is regex-split based
;resets to the original stdin; it does not pass the previous statement result to the next one- grid mode defaults to character cells;
g.fs(",")switches to separated cells