GNU awk FS Regular Expression Behavior with Variable-Length Delimiters
29K reputation · 17 Apr 2022, 20:04 UTC
GNU awk (gawk) allows the Field Splitter (FS) to be defined as a regular expression, enabling the division of records based on complex patterns rather than single characters. This is particularly useful when processing logs with inconsistent whitespace or multi-character delimiters.
A design uncertainty arises when FS is set to a regex that matches variable-length strings. While gawk identifies the fields based on the pattern, the interaction between the NF (Number of Fields) calculation and the exact boundary of a greedy regex match can vary depending on the input stream's structure.
What is the deterministic behavior of FS when a regular expression matches an empty string or overlapping patterns? How does gawk handle the field indexing when the delimiter regex results in zero-width matches between characters?
1 answer
1 question comment
Use comments to ask for clarification. Post a solution as an answer.
29,025 reputation · 18 Apr 2022, 04:04 UTC
When FS is a regular expression, gawk scans the record left‑to‑right and takes the longest possible non‑overlapping match as a delimiter. After a match is consumed, the search pointer jumps to the first character after that match; overlapping matches are never considered, even if the pattern could match a substring that starts inside the previous match.
For zero‑width patterns (e.g., "", "^", or look‑ahead assertions), gawk prevents an infinite loop by advancing the pointer by at least one character when no non‑zero length match is found. This advance creates a field boundary, but no empty field is generated unless the zero‑width match occurs at the very start or end of the record.
- Example:
echo 'ab' | awk -F'^' '{print NF, $1, $2, $3}'yieldsNF=3with fields"","a","b". - If the regex can match both zero and non‑zero strings (e.g.,
"[[:space:]]*"), gawk prefers the longest non‑zero match; only when no whitespace is present does it advance by one character to avoid looping.