GNU Recutils
GNU recutils is a set of tools for managing human-readable, plain-text databases. Records are stored in .rec files with a simple field: value syntax.
Quick Reference
Core Commands
| Command | Purpose |
|---|---|
recsel | Select and query records |
recins | Insert new records |
recdel | Delete records |
recset | Modify field values |
recfix | Validate, check integrity, sort |
recfmt | Format output with templates |
recinf | Print information about rec files |
rec2csv | Convert to CSV |
csv2rec | Convert from CSV |
Basic File Format
# Comment line Field_name: field value Another_field: another value Field_name: second record Another_field: its value
Records are separated by blank lines. Field names are case-sensitive and conventionally capitalized.
Multi-line Values
Use + continuation for multi-line content:
Description: This is the first line + and this continues on the second line + and a third line too.
Or use backslash at end of line:
Description: This is a long value that \ continues on the next line.
Schema Definitions (%rec Descriptors)
Place schema declarations before records:
%rec: Entity %key: Id %mandatory: Id Name %allowed: Id Name Prototype Description OnLook OnTouch %type: Prototype rec Entity %type: Count int %type: Active bool %unique: Name Id: sword Name: Iron Sword Count: 1 Active: yes
Common Descriptors
| Descriptor | Purpose | Example |
|---|---|---|
%rec: Type | Names the record type | %rec: Entity |
%key: field | Primary key (unique, mandatory) | %key: Id |
%mandatory: f1 f2 | Required fields | %mandatory: Id Name |
%allowed: f1 f2 | Whitelist of valid fields | %allowed: Id Name Desc |
%prohibit: f1 | Forbidden fields | %prohibit: Password |
%unique: field | Field must be unique | %unique: Email |
%type: field type | Field type constraint | %type: Count int |
%auto: field | Auto-generated field | %auto: Created_at |
%sort: field | Default sort order | %sort: Name |
Field Types
| Type | Description | Example Values |
|---|---|---|
int | Integer | 42, -7 |
real | Floating point | 3.14, -2.5 |
bool | Boolean | yes, no, true, false, 1, 0 |
line | Single line (no newlines) | Hello world |
date | ISO 8601 date | 2024-01-15 |
email | Email address | user@example.com |
uuid | UUID | 550e8400-e29b-41d4-a716-446655440000 |
rec Type | Foreign key reference | References another record type |
enum A B C | Enumeration | One of the listed values |
regexp /pattern/ | Regex pattern | Must match pattern |
size N | Max size in bytes | String length limit |
range MIN MAX | Numeric range | range 1 100 |
Foreign Key References
%rec: Room %key: Id Id: tavern Name: The Rusty Tavern %rec: Entity %key: Id %type: Room rec Room Id: mug Name: Beer Mug Room: tavern
Entity Containment (MUDD)
Entities can be nested using a Container field:
%rec: Entity %key: Id %type: Prototype rec Entity %type: Container rec Entity Id: table Name: Wooden Table Prototype: furniture Id: lamp Name: Brass Lamp Prototype: object Container: table
The lamp is "on" the table. Recfix validates that Container references exist.
Common Operations
Query Records
# All records recsel data.rec # Filter by field value recsel -e 'Name = "sword"' data.rec # Pattern matching recsel -e 'Name ~ "Iron"' data.rec # Numeric comparison recsel -e 'Count > 5' data.rec # Multiple conditions recsel -e 'Type = "weapon" && Count > 0' data.rec # Select specific fields recsel -p Id,Name data.rec # Count matching records recsel -c -e 'Active = yes' data.rec # Select by record type recsel -t Entity data.rec
Insert Records
# Insert from stdin echo -e "Id: axe\nName: Battle Axe" | recins data.rec # Insert with field values recins -f Id -v sword -f Name -v "Iron Sword" data.rec
Modify Records
# Update field value recset -e 'Id = "sword"' -f Count -s 5 data.rec # Delete field from record recset -e 'Id = "sword"' -f Obsolete -d data.rec
Delete Records
# Delete matching records recdel -e 'Count = 0' data.rec # Delete by record number recdel -n 3 data.rec
Validate
# Check file integrity recfix data.rec # Check and report all errors recfix --check data.rec
Convert Formats
# To CSV rec2csv data.rec > data.csv # From CSV csv2rec data.csv > data.rec
Format Output
# Custom output format
recsel data.rec | recfmt '{{Id}}: {{Name}}\n'
# Template-based formatting (recfmt reads from stdin)
recsel data.rec | recfmt -f template.fmt
Expression Syntax
Used with -e flag in recsel, recdel, recset:
| Operator | Meaning | Example |
|---|---|---|
= | Equals | Name = "sword" |
!= | Not equals | Type != "armor" |
<, >, <=, >= | Numeric comparison | Count > 5 |
~ | Regex match | Name ~ "^Iron" |
&& | Logical AND | Type = "weapon" && Rare = yes |
| ` | ` | |
! | Logical NOT | ! (Count = 0) |
#field | Field count (0 if absent) | #Description |
Tips
- •Use
recfix --checkbefore loading data to catch schema violations - •Foreign keys (
%type: Field rec OtherType) validate references exist - •Field names start with a letter and contain only
[a-zA-Z0-9_]; special fields start with% - •Empty lines separate records; use
+continuation for multi-line values - •Comments start with
#at the beginning of a line - •For JSON output, pipe through
recsel -pand parse with a script
MUDD Naming Convention
In this project, entity fields use PascalCase (e.g., DescriptionShort, OnAttack). This avoids visual noise from underscores. See data/entities.rec for examples.