Practical regular expressions for agents: core syntax, the grep/Python/sed dialect differences that actually bite, and tested patterns for timestamps, UUIDs, IPs, emails, and log parsing. Trigger when extracting data from logs or text, or rewriting text with patterns.
./install.sh regex-masteryname: "regex_mastery" description: "Practical regular expressions for agents: core syntax, the grep/Python/sed dialect differences that actually bite, and tested patterns for timestamps, UUIDs, IPs, emails, and log parsing. Trigger when extracting data from logs or text, or rewriting text with patterns."
Give agents a working regex toolkit: the core syntax, which dialect they're in (this is where most bugs live), and copy-paste patterns that were actually tested. Every pattern below was verified against real input before inclusion.
grep -o, a quick python3 -c, or sed on a sample line) and eyeball the matches. A pattern that "looks right" and one that works are different things.^$ or \b guards so it matches what you mean, not a substring of something else.re with named groups — the result is self-documenting and easy to act on.[abc], [^abc], [0-9], [a-zA-Z]; shorthands \d \w \s (+ \D \W \S); . = any char except newline (escape it: \.). (0+), + (1+), ? (0/1), {n}, {n,}, {n,m} — all greedy by default; add ? for lazy: <.> grabs <a><b>, <.?> grabs <a> then <b>.^ start, $ end, \b word boundary. re.findall(r'^\d+$', '1\n2', re.M) matches each line.(...) captures; (?:...) non-capturing; (?P<name>...) named; \1 backreferences the capture.cat|dog — scope it with groups when needed: \b(cat|dog)s?\b.re.DOTALL lets . cross newlines; re.MULTILINE makes ^$ per-line.| Tool | Default dialect | What works | |---|---|---| | grep | BRE: ( ) { } are literal unless escaped as \( \) \{ \}; + and ? are literal | Use BRE escapes, or... | | grep -E | ERE: + ? () \| {} work unescaped; no \d \w \b | Default to -E for ad-hoc searching | | grep -P | PCRE: \d \w \s \b, lookarounds | Works here, but treat as non-portable (absent on BSD/macOS grep) — default to -E | | sed | BRE by default; -E enables ERE | -E is portable (both GNU -E and -r work); replacement uses \1 backreferences and & for the whole match | | Python re | PCRE-like | Always use raw strings: r'\d+'. Default to re.search (finds anywhere); re.match only matches at the string start — a classic silent bug. |
Tested examples:
bash
echo "abcabc" | grep -E "(abc)\1" # ERE backreference
echo "2026-09-17" | sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/' # -> 17/09/2026
\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:Z|[+-]\d{2}:?\d{2})?\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b (matches octet-shaped text; 999.1.1.1 passes — validate numerically if strictness matters)(?<![0-9A-Fa-f:])(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,6}(?::[0-9A-Fa-f]{1,4}){1,}|(?:[0-9A-Fa-f]{1,4}:){1,7}:|:(?::[0-9A-Fa-f]{1,4}){1,7})(?![0-9A-Fa-f:])[\w.+-]+@[\w-]+\.[\w.-]+Why strict email regex is a trap: full RFC validation is thousands of chars and still wrong at the edges (quoted locals, IP literals, new TLDs). Default to the simple pattern above plus a real verification step (send, or a lookup) — a regex can only tell you it looks like an email.
python
import re
line = '[2026-09-17 02:54:59] ERROR worker-3: connection refused (10.0.0.5:8080)'
m = re.search(r'\[(?P<ts>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\]\s+(?P<level>[A-Z]+)\s+(?P<msg>.*)', line)
m.groupdict()
# {'ts': '2026-09-17 02:54:59', 'level': 'ERROR', 'msg': 'worker-3: connection refused (10.0.0.5:8080)'}
grep -E over plain grep and over grep -P; default to raw strings in Python; default to re.search over re.match.\.) — unescaped, 10.0.0.5 also matches 10x0y0z5.. spanning more than intended; reach for .? or a negated class ([^]]) in delimited text.publisher key 2e43c913833850e6d80f939677e3de76e806d47785c0f1caaca90f9d18c98e1e
No ratings yet — be the first to rate it.