AWK — 18 Operations for AI Agents

AWK programs are the text-processing workhorses of operations — log extraction, ETL glue, one-liners that became infrastructure. act101 reads patterns and actions as structure, so agents maintain scripts that long outlived their authors.

This page is the canonical reference an AI coding agent uses to refactor, query, and analyze AWK code through the act MCP server. 18 operations available: 0 refactor, 18 query, 0 analysis. Each operation is callable from Claude Code, Cursor, Codex, OpenCode, or any MCP-compatible agent host. Click any operation for a stable anchor link suitable for citation.

18Query

Worked AWK examples

act101 reads an AWK script's function definitions as function declarations, and treats the BEGIN and END special patterns the same way, naming each declaration for the pattern keyword itself. symbols covers the same declarations and adds every parameter name a function declares, plus any variable bound by a for (x in array) loop, all reported as kind: unknown since awk's grammar carries no parameter or loop-variable kind of its own. A bare pattern-action rule with no BEGIN/END keyword — the record-processing block that runs on every input line — produces no declaration at all in either query. The unit of structure in this grammar is the top-level rule: neither query descends into a rule's own statements, so a variable assigned only inside one, like bounce_total, never appears. Each example below is the verbatim output of the command shown, run against the file shown. Query outputs are pretty-printed with the timing block omitted.

Read the script's functions and special patterns as a skeleton

mail-log-triage.awk classifies mail relay log lines: helper functions is_bounce and severity decide each line's status, and the BEGIN/END patterns handle field-splitting and the per-relay bounce report.

$ act query skeleton mail-log-triage.awk

Before

#!/usr/bin/awk -f
function is_bounce(status) {
    return status ~ /^5[0-9][0-9]$/
}

function severity(status,    level) {
    if (status ~ /^4/) level = "deferred"
    else if (is_bounce(status)) level = "bounce"
    else level = "ok"
    return level
}

BEGIN { FS = "\t"; bounce_total = 0 }

{
    level = severity($3)
    if (level == "bounce") {
        bounce_total++
        bounces[$2]++
    }
}

END {
    for (relay in bounces)
        printf "%s\t%d bounces\n", relay, bounces[relay]
    printf "total\t%d\n", bounce_total
}

Output

{
    "type": "Skeleton",
    "declarations": [
        {
            "kind": "function",
            "name": "is_bounce",
            "range": {
                "start": {
                    "file": "mail-log-triage.awk",
                    "line": 2,
                    "column": 1,
                    "byte_offset": 18
                },
                "end": {
                    "file": "mail-log-triage.awk",
                    "line": 4,
                    "column": 2,
                    "byte_offset": 84
                }
            },
            "name_range": {
                "start": {
                    "file": "mail-log-triage.awk",
                    "line": 2,
                    "column": 10,
                    "byte_offset": 27
                },
                "end": {
                    "file": "mail-log-triage.awk",
                    "line": 2,
                    "column": 19,
                    "byte_offset": 36
                }
            }
        },
        {
            "kind": "function",
            "name": "severity",
            "range": {
                "start": {
                    "file": "mail-log-triage.awk",
                    "line": 6,
                    "column": 1,
                    "byte_offset": 86
                },
                "end": {
                    "file": "mail-log-triage.awk",
                    "line": 11,
                    "column": 2,
                    "byte_offset": 255
                }
            },
            "name_range": {
                "start": {
                    "file": "mail-log-triage.awk",
                    "line": 6,
                    "column": 10,
                    "byte_offset": 95
                },
                "end": {
                    "file": "mail-log-triage.awk",
                    "line": 6,
                    "column": 18,
                    "byte_offset": 103
                }
            }
        },
        {
            "kind": "function",
            "name": "BEGIN",
            "range": {
                "start": {
                    "file": "mail-log-triage.awk",
                    "line": 13,
                    "column": 1,
                    "byte_offset": 257
                },
                "end": {
                    "file": "mail-log-triage.awk",
                    "line": 13,
                    "column": 38,
                    "byte_offset": 294
                }
            },
            "name_range": {
                "start": {
                    "file": "mail-log-triage.awk",
                    "line": 13,
                    "column": 1,
                    "byte_offset": 257
                },
                "end": {
                    "file": "mail-log-triage.awk",
                    "line": 13,
                    "column": 6,
                    "byte_offset": 262
                }
            }
        },
        {
            "kind": "function",
            "name": "END",
            "range": {
                "start": {
                    "file": "mail-log-triage.awk",
                    "line": 23,
                    "column": 1,
                    "byte_offset": 406
                },
                "end": {
                    "file": "mail-log-triage.awk",
                    "line": 27,
                    "column": 2,
                    "byte_offset": 536
                }
            },
            "name_range": {
                "start": {
                    "file": "mail-log-triage.awk",
                    "line": 23,
                    "column": 1,
                    "byte_offset": 406
                },
                "end": {
                    "file": "mail-log-triage.awk",
                    "line": 23,
                    "column": 4,
                    "byte_offset": 409
                }
            }
        }
    ]
}

The skeleton reports four declarations: is_bounce and severity as function, and BEGIN and END as function too, named for the pattern keyword itself; the plain pattern-action rule that runs on every log line in between has no name and doesn't appear.

List the functions, their parameters, and the loop variable as symbols

is_bounce takes one parameter, severity takes two — the second used as a local — and END walks the bounces array with for (relay in bounces).

$ act query symbols mail-log-triage.awk

Before

#!/usr/bin/awk -f
function is_bounce(status) {
    return status ~ /^5[0-9][0-9]$/
}

function severity(status,    level) {
    if (status ~ /^4/) level = "deferred"
    else if (is_bounce(status)) level = "bounce"
    else level = "ok"
    return level
}

BEGIN { FS = "\t"; bounce_total = 0 }

{
    level = severity($3)
    if (level == "bounce") {
        bounce_total++
        bounces[$2]++
    }
}

END {
    for (relay in bounces)
        printf "%s\t%d bounces\n", relay, bounces[relay]
    printf "total\t%d\n", bounce_total
}

Output

{
    "type": "Symbols",
    "symbols": [
        {
            "name": "is_bounce",
            "kind": "function",
            "range": {
                "start": {
                    "file": "mail-log-triage.awk",
                    "line": 2,
                    "column": 10,
                    "byte_offset": 27
                },
                "end": {
                    "file": "mail-log-triage.awk",
                    "line": 2,
                    "column": 19,
                    "byte_offset": 36
                }
            },
            "visibility": "unknown"
        },
        {
            "name": "status",
            "kind": "unknown",
            "range": {
                "start": {
                    "file": "mail-log-triage.awk",
                    "line": 2,
                    "column": 20,
                    "byte_offset": 37
                },
                "end": {
                    "file": "mail-log-triage.awk",
                    "line": 2,
                    "column": 26,
                    "byte_offset": 43
                }
            },
            "visibility": "unknown"
        },
        {
            "name": "severity",
            "kind": "function",
            "range": {
                "start": {
                    "file": "mail-log-triage.awk",
                    "line": 6,
                    "column": 10,
                    "byte_offset": 95
                },
                "end": {
                    "file": "mail-log-triage.awk",
                    "line": 6,
                    "column": 18,
                    "byte_offset": 103
                }
            },
            "visibility": "unknown"
        },
        {
            "name": "status",
            "kind": "unknown",
            "range": {
                "start": {
                    "file": "mail-log-triage.awk",
                    "line": 6,
                    "column": 19,
                    "byte_offset": 104
                },
                "end": {
                    "file": "mail-log-triage.awk",
                    "line": 6,
                    "column": 25,
                    "byte_offset": 110
                }
            },
            "visibility": "unknown"
        },
        {
            "name": "level",
            "kind": "unknown",
            "range": {
                "start": {
                    "file": "mail-log-triage.awk",
                    "line": 6,
                    "column": 30,
                    "byte_offset": 115
                },
                "end": {
                    "file": "mail-log-triage.awk",
                    "line": 6,
                    "column": 35,
                    "byte_offset": 120
                }
            },
            "visibility": "unknown"
        },
        {
            "name": "relay",
            "kind": "unknown",
            "range": {
                "start": {
                    "file": "mail-log-triage.awk",
                    "line": 24,
                    "column": 10,
                    "byte_offset": 421
                },
                "end": {
                    "file": "mail-log-triage.awk",
                    "line": 24,
                    "column": 15,
                    "byte_offset": 426
                }
            },
            "visibility": "unknown"
        }
    ]
}

symbols adds four kind: unknown entries to the two functions: status from is_bounce's parameter list, status and level from severity's, and relay from the for (relay in bounces) loop; bounce_total, only ever assigned inside BEGIN and the main rule, does not appear.

Score the triage logic's branching

severity chains an if/else if/else over the status code, the main rule wraps its bounce count in one more if, and END loops over the bounces array.

$ act query complexity mail-log-triage.awk

Before

#!/usr/bin/awk -f
function is_bounce(status) {
    return status ~ /^5[0-9][0-9]$/
}

function severity(status,    level) {
    if (status ~ /^4/) level = "deferred"
    else if (is_bounce(status)) level = "bounce"
    else level = "ok"
    return level
}

BEGIN { FS = "\t"; bounce_total = 0 }

{
    level = severity($3)
    if (level == "bounce") {
        bounce_total++
        bounces[$2]++
    }
}

END {
    for (relay in bounces)
        printf "%s\t%d bounces\n", relay, bounces[relay]
    printf "total\t%d\n", bounce_total
}

Output

{
    "type": "Complexity",
    "score": 5,
    "details": "Base complexity: 1, +1 if_statement, +1 if_statement, +1 if_statement, +1 for_in_statement"
}

Complexity starts at a base of 1 and adds one point for each of three if_statement nodes — the if and its else if inside severity, plus the main rule's if (level == "bounce") — and one more for the for_in_statement in END, for a total score of 5.

Query

18 query tools, the same on every supported language. Descriptions live in the shared reference: /docs/query-tools.

callers control_flow data_flow definition diagnostics effect_closure effect_summary fix_auto get_type graph import_organize interface mutations references repo_outline skeleton symbols symbols_batch

← AstroBash →