cswork / libraryOpen practice workspace

Articles

AWK Interview Questions: Field Splitting, Associative Arrays, and Record Processing

Practice AWK field splitting, associative arrays, record rebuilding, and empty-file joins with verified fixtures and five related interview questions.

An AWK command works on a sample file, then silently treats every event as a lookup row when the lookup file is empty. Another command collapses meaningful empty fields. A third combines two different keys into the same array entry. These failures come from assumptions about records and identity, not from missing a clever command.

This AWK interview guide uses original fixtures to make field splitting, associative arrays, and file boundaries visible. Official language behavior is linked to primary references; verified outputs come from macOS /usr/bin/awk, reporting version 20200816. These checks are not a GNU awk compatibility claim or an employer's interview syllabus.

What happens to a record before your action runs?

The official FreeBSD awk manual describes pattern-action processing, records, numbered fields, and built-in counters. Normally, a newline separates records; $0 is the record and $1 onward are its fields. NF counts fields, FNR counts records in the current file, and NR counts records across files.

For an original review, annotate the lifecycle: read one record, split its fields using the input separator, evaluate patterns in program order, run matching actions, then advance. A record can match more than one rule. If your validation rule prints a rejection but does not skip the remaining rules, a later aggregation rule can still accept the same malformed record.

For practice, put next after a rejected record when subsequent rules must not process it. Explain that choice in terms of control flow. Printing “invalid” is an observation; it is not a control-flow operation.

Also distinguish a default action from a default pattern. A bare matching pattern prints the current record. An action without a pattern applies to each record. Those conveniences can make compact code readable, but in a review they can hide whether a line was deliberately retained or merely fell through.

Before coding, ask whether the input has headers, blank lines, comments, or multiline records. The field count for a valid business record does not necessarily apply to a header or a comment. State which records are excluded and why.

Why is a space separator special?

The official One True Awk manual source distinguishes the special separator FS=" " from ordinary separators. The special space value ignores leading and trailing whitespace and groups runs of whitespace. A separator regexp such as [ ] treats individual spaces differently.

Our original fixture contains two leading spaces, two spaces between letters, and one trailing space:

  a  b 

These commands produced different field counts in our local run:

awk '{ print NF, "[" $1 "]", "[" $2 "]" }' sample.txt
awk -F '[ ]' '{ print NF, "[" $1 "]", "[" $3 "]", "[" $6 "]" }' sample.txt

The first output was 2 [a] [b]; the second was 6 [] [a] []. Do not describe -F ' ' as a way to split on one literal space: it selects the special space behavior. The regexp makes the intended distinction explicit.

For the original record a,,c,, -F ',' produced four fields, with the second and fourth empty. That is useful for a simple delimiter-separated format, but it is not a general CSV parser. A quoted comma or quoted newline changes the parsing problem.

Current One True Awk has an explicit CSV mode documented in its official repository. Our older local binary was not tested with that mode. In an interview, identify the installed implementation before relying on newer options. A portable simple-field exercise and a CSV exercise should not have an unstated shared input contract.

When does OFS change the output?

FS controls interpretation of input; OFS controls how separately printed expressions and rebuilt fields are separated. Changing OFS alone does not promise that the raw record's spacing has been normalized. The One True Awk manual documents these variables.

Use this original transformation:

{
    raw = $0
    $1 = $1
    print raw
    print $0
}

With OFS="|" and input a b , our local output preserved the original first line and produced a|b on the second. Assigning to a field caused reconstruction. Saving raw beforehand retained the original evidence.

The interview consequence is concrete: a command intended to change one value can normalize the entire record's separator layout. If exact spacing is part of a configuration file's meaning or readability, decide whether field reconstruction is acceptable.

Compare print $0, print $1,$2, and printf "%s|%s\n", $1,$2 conceptually. The first emits a record, the second assembles expressions using output separators, and the third uses an explicit format. When a user-controlled string is data, keep it as an argument to a constant format instead of treating it as the format itself.

You can test this boundary without a large dataset. Include repeated spaces, leading whitespace, trailing whitespace, and empty fields. Check complete expected strings, not merely whether a desired word appears somewhere in the output.

How should you use associative arrays?

For an original count exercise, count[$1]++ groups by the first field. The important question is whether that field alone is the identity you need. If the same customer ID occurs in two tenants, grouping only by customer ID mixes different customers and changes their totals.

Membership also differs from truthiness. Our local fixture set value["a"]=0. The expression "a" in value was true, while testing value["a"] as a Boolean was false. Use membership when zero or an empty string is a valid stored value.

This distinction matters in lookup joins. A discount of zero means a known customer with no discount; an absent key means no lookup result. Replacing both with “missing” corrupts the business meaning.

Avoid depending on array traversal order for expected reports. If the output contract requires alphabetical keys, add a deliberate ordering step or use an implementation-specific facility that you name. Our portable examples do not promise ordered for (key in array) output.

For memory reasoning, relate state to distinct keys rather than input lines. A one-pass count over a billion records can be manageable with a few hundred keys, while a smaller file with a unique key per record can exhaust memory. Ask about key cardinality before calling an array-based solution “streaming.”

Can a multidimensional key collide?

The official FreeBSD awk manual explains that comma-separated subscripts are joined using SUBSEP. They are not automatically a collision-proof tuple encoding.

Our original counterexample makes the separator visible:

BEGIN {
    SUBSEP = "|"
    value["x|y", "z"] = 1
    value["x", "y|z"] = 2
    print value["x|y", "z"]
}

The local output was 2. Both key pairs become x|y|z, so the second assignment overwrites the first. Choosing an unusual separator lowers the chance of accidental collision but does not prove uniqueness if arbitrary input can contain that separator.

For this simple exercise, either reject separator-bearing key components or define an unambiguous encoding. A length-prefixed pair is one possibility when both components are treated as strings and the encoding is specified consistently. Explain how decoding works; changing the separator midway through processing is not a repair for entries already stored.

Distinguish domain restrictions from implementation convenience. If tenant and customer keys are validated to contain only digits, a non-digit delimiter can be justified by that contract. If keys are arbitrary bytes, state the stronger encoding requirement instead.

Why does NR == FNR fail with an empty first file?

This original two-file idiom loads a lookup table and then processes events:

NR == FNR { label[$1] = $2; next }
{ print $1, label[$1], $2 }

Our empty-lookup test silently produced no event output. Because the first file contained no records, the first event in the second file still had NR==FNR. Every event was mistaken for a lookup record and skipped.

The counters describe records, not an explicit file-processing phase. A passing nonempty sample does not establish the assumption that the lookup file always contains at least one record.

For a portable original repair, make the phase explicit in command-line assignments:

awk -F ',' -v OFS=',' '
mode == "lookup" { label[$1] = $2; next }
mode == "events" {
    if ($1 in label) print $1, label[$1], $2
    else print $1, "MISSING", $2
}
' mode=lookup labels.csv mode=events events.csv

With events u1,4 and u2,7, an empty lookup produced u1,MISSING,4 and u2,MISSING,7. A lookup containing u1,A produced u1,A,4 and u2,MISSING,7. Both outcomes were verified in the saved local fixtures.

This teaching sketch assumes simple comma-separated fields without quotes, no headers, and a lookup key policy. Duplicate lookup keys currently use the last value. If that is unacceptable, reject duplicates explicitly before assigning. Do not claim the sketch validates a production CSV schema.

A filename-based condition can also work for distinct known files. Explain what happens if the same path is supplied twice or input comes from standard input. The explicit phase approach expresses the actual distinction the program needs.

How do you validate record processing without hiding failures?

Use an original aggregation input with valid a,2,ok, malformed b,x,ok, and valid a,3,ok. Before adding the numeric field, check field count and the exact allowed numeric syntax. Simply coercing x into a number can turn invalid input into a plausible zero.

Choose whether rejection makes the whole operation fail or creates a separate reject stream. That is a product decision. If partial totals are allowed, identify them as partial and retain rejected records for review. If completeness is required, a nonzero exit status should prevent a caller from treating the report as final.

For golden fixtures, include an empty file, one valid row, a fourth field caused by an extra delimiter, a missing value, zero, a duplicate lookup key, and the separator-collision pair. Compare both the data result and the exit status. A correct-looking total with rejected rows can still violate the completeness contract.

Our saved check harness verified nine focused invariants: both whitespace modes, comma empties, reconstruction, membership, key collision, the empty-first-file failure, and explicit-phase behavior with empty and populated lookup files. It did not test every AWK implementation or large-file performance.

The strongest preparation answer connects each fixture to an assumption. “This test protects empty lookup input” is more informative than “I added edge cases.” If a requirement changes to quoted CSV or structured JSON, revisit the parser rather than extending the delimiter trick indefinitely.

Sources and Further Reading