0Pricing
DevOps Bootcamp · Lesson

Patterns, Ranges, and BEGIN/END Blocks

Filter records with conditional patterns and produce headers and totals using BEGIN and END blocks.

Patterns, Ranges, and BEGIN/END Blocks is a free DevOps Bootcamp lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the DevOps Bootcamp learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What Are awk Patterns?

In awk, a pattern is a condition that controls whether an action block executes for a given input line. The general form is:

awk 'pattern { action }' file

If the pattern matches the current record, the action runs. If no action is given, awk prints the entire line by default. Patterns can be:

  • Regular expressions/regex/
  • Relational expressions$3 > 100
  • Compound expressions$1 == "ERROR" && $4 > 500
  • Range patterns/start/,/end/
  • Special patternsBEGIN and END

Understanding patterns is the gateway to using awk as a powerful data filter rather than just a column printer.

Regex Patterns to Filter Lines

The most common pattern type is a regular expression enclosed in forward slashes. awk tests each line against the regex and runs the action only on matches.

Below we filter an /var/log/syslog-style file to show only lines containing the word ERROR:

#!/usr/bin/env bash
# Simulate a log file and filter ERROR lines
log=$(cat <<'EOF'
2026-06-11 08:01:22 INFO  service started
2026-06-11 08:02:05 ERROR disk quota exceeded on /dev/sda1
2026-06-11 08:02:44 WARN  memory usage at 80%
2026-06-11 08:03:10 ERROR connection timeout to 10.0.0.5
2026-06-11 08:03:55 INFO  backup completed
EOF
)

echo "=== ERROR lines only ==="
echo "$log" | awk '/ERROR/ { print NR": "$0 }'

Negated and Compound Patterns

Prefix a regex pattern with ! to invert it — matching lines that do not contain the pattern. Combine multiple conditions with && (AND) or || (OR) for fine-grained control.

  • !/DEBUG/ — skip debug lines
  • $3 > 500 && $5 == "POST" — large POST requests
  • /WARN/ || /ERROR/ — either severity

The example below parses a simplified access log and prints only POST requests with a response size above 1000 bytes:

#!/usr/bin/env bash
access=$(cat <<'EOF'
10.0.0.1 GET  /api/ping     200  48
10.0.0.2 POST /api/upload   201  2048
10.0.0.3 GET  /api/users    200  512
10.0.0.4 POST /api/data     200  3500
10.0.0.5 POST /api/health   200  60
EOF
)

echo "Large POST requests (>1000 bytes):"
echo "$access" | awk '$2 == "POST" && $5 > 1000 { print $1, $3, "size="$5 }'

Range Patterns: /start/,/end/

A range pattern selects a contiguous block of records — from the line matching /start/ through the line matching /end/, inclusive. The syntax is:

awk '/start/,/end/ { action }' file

Key behaviours to know:

  • The range activates on the first line that matches /start/ and deactivates after the first subsequent line matching /end/.
  • If /end/ never matches, the range stays active until EOF.
  • Multiple non-overlapping ranges in the same file are each processed independently.

This makes range patterns ideal for extracting marked sections from config files, logs with delimiter headers, or multi-block reports.

Extracting Sections with Range Patterns

Consider a server config that has multiple named sections delimited by [section] headers. We want only the lines inside [database]:

#!/usr/bin/env bash
config=$(cat <<'EOF'
[server]
host = 0.0.0.0
port = 8080

[database]
host = 127.0.0.1
port = 5432
name = appdb
user = admin

[cache]
host = 127.0.0.1
port = 6379
EOF
)

echo "=== [database] section ==="
echo "$config" | awk '/\[database\]/,/^\[/' \
  | awk 'NR > 1 && !/^\[/ && NF'

The BEGIN Block

The BEGIN block runs once before any input is read. It is ideal for:

  • Printing a report header
  • Initialising variables and counters
  • Setting the field separator (FS) or other built-in variables
  • Running stand-alone awk programs that need no input

Syntax:

awk 'BEGIN { setup } pattern { action }' file

Setting FS in BEGIN is the clean alternative to the -F flag — particularly useful when writing multi-rule programs where the separator logic lives inside the script.

#!/usr/bin/env bash
csv=$(cat <<'EOF'
alice,engineering,95000
bob,marketing,72000
carol,engineering,105000
dave,hr,68000
EOF
)

echo "$csv" | awk 'BEGIN {
  FS = ","
  printf "%-10s %-15s %10s\n", "Name", "Department", "Salary"
  print "--------------------------------------------"
}'

The END Block

The END block runs once after the last record is processed. It is the natural place to:

  • Print totals, averages, and summaries accumulated during processing
  • Flush or close output files
  • Print footer lines for formatted reports

Variables set during the main rules are still in scope inside END — this is how running totals work. The example below sums salary values from a CSV and prints the total in the footer:

#!/usr/bin/env bash
csv=$(cat <<'EOF'
alice,engineering,95000
bob,marketing,72000
carol,engineering,105000
dave,hr,68000
EOF
)

echo "$csv" | awk -F',' '
  { total += $3; count++ }
  END { printf "Employees: %d  Total payroll: $%d\n", count, total }
'

Combining BEGIN, Rules, and END

The real power emerges when you combine all three blocks into a single self-contained awk program that prints a header, processes each record, and prints a summary. This pattern is the backbone of shell-based reporting pipelines.

Below is a complete report generator that reads a CSV of web requests and prints per-line details plus a final count of requests that exceeded 1 second:

#!/usr/bin/env bash
data=$(cat <<'EOF'
/api/ping,0.03
/api/users,1.45
/api/login,0.88
/api/upload,2.10
/api/health,0.02
/api/report,1.73
EOF
)

echo "$data" | awk -F',' '
BEGIN {
  printf "%-20s %10s %s\n", "Endpoint", "Latency(s)", "Status"
  print "--------------------------------------"
}
{
  status = ($2 > 1.0) ? "SLOW" : "OK"
  if (status == "SLOW") slow++
  printf "%-20s %10s %s\n", $1, $2, status
}
END {
  print "--------------------------------------"
  printf "Slow requests: %d / %d\n", slow, NR
}
'

Using BEGIN to Set OFS and ORS

Two often-overlooked output variables can be set cleanly in BEGIN:

  • OFS (Output Field Separator) — inserted between fields when you rebuild a record with $1=$1 or use print $1, $2
  • ORS (Output Record Separator) — appended after each print call (default is \n)

Setting OFS="," lets you reformat a TSV into CSV in one pass without string concatenation. Changing ORS to "\n---\n" adds dividers between records automatically.

#!/usr/bin/env bash
# Convert whitespace-delimited data to CSV with a header
data=$(cat <<'EOF'
alice   engineering  95000
bob     marketing    72000
carol   engineering  105000
EOF
)

echo "$data" | awk 'BEGIN { OFS=","; print "name,dept,salary" }
{ $1=$1; print }'

Practical Range Pattern: Log Incident Windows

Range patterns shine in operational log analysis. A common task is extracting all log lines between the start and end of an incident, identified by sentinel messages like INCIDENT START and INCIDENT END.

In the example below, awk extracts only the lines inside the incident window and counts the ERROR events within it — combining a range pattern with an accumulator and an END summary:

#!/usr/bin/env bash
log=$(cat <<'EOF'
08:00:01 INFO  Normal operation
08:01:00 INFO  INCIDENT START: disk pressure detected
08:01:05 ERROR write failed on /dev/sda1
08:01:09 ERROR inode table corruption
08:01:14 WARN  remounting read-only
08:01:22 INFO  INCIDENT END: disk replaced
08:02:00 INFO  Normal operation resumed
EOF
)

echo "$log" | awk '
/INCIDENT START/,/INCIDENT END/ {
  print
  if (/ERROR/) errors++
}
END { print "Errors during incident:", errors+0 }
'

Skipping the Delimiter Lines in a Range

By default, range patterns include the lines that match /start/ and /end/ themselves. To skip those delimiter lines and keep only the content in between, guard the action with a negation or a flag variable:

  • Negation approach: /start/,/end/ { if (!/start/ && !/end/) print }
  • Flag approach: manually set a variable to 1 on /start/ and 0 on /end/

The flag approach is more robust when the delimiter text might also appear in the body content:

#!/usr/bin/env bash
doc=$(cat <<'EOF'
## preamble
some intro text
## BEGIN_REPORT
row1: alpha 100
row2: beta  200
row3: gamma 150
## END_REPORT
## appendix
EOF
)

echo "$doc" | awk '
/## BEGIN_REPORT/ { capture=1; next }
/## END_REPORT/   { capture=0 }
capture           { print }
'

Knowledge Check: BEGIN Block Behavior

Test your understanding of how BEGIN and END blocks interact with input processing in awk.

Lesson Recap: Patterns, Ranges, and BEGIN/END

In this lesson you mastered the three most powerful structural features of awk programs:

  • Patterns — regex (/ERROR/), relational ($3 > 1000), negated (!/DEBUG/), and compound (&&, ||) — control which records trigger an action without needing explicit if statements.
  • Range patterns (/start/,/end/) — select contiguous blocks of records, perfect for extracting log incident windows, config sections, and report segments. Use a flag variable (next + boolean) when you need to exclude the delimiter lines themselves.
  • BEGIN block — runs once before any input; ideal for setting FS, OFS, ORS, initialising counters, and printing report headers.
  • END block — runs once after all input; the right place to print totals, averages, and footers using variables accumulated in the main rules.

Together these features let you write complete, self-documenting awk programs that ingest raw text and emit structured, human-readable reports — all in a single pipeline stage without temporary files or external tools.

Frequently asked questions

Is the “Patterns, Ranges, and BEGIN/END Blocks” lesson free?

Yes — the full text of “Patterns, Ranges, and BEGIN/END Blocks” is free to read here on the web, and the DevOps Bootcamp course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the DevOps Bootcamp course, upgrade to CoddyKit PRO.

What will I learn in “Patterns, Ranges, and BEGIN/END Blocks”?

Filter records with conditional patterns and produce headers and totals using BEGIN and END blocks. You practise DevOps Bootcamp with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start DevOps Bootcamp?

No prior experience is required. DevOps Bootcamp on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Patterns, Ranges, and BEGIN/END Blocks” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this DevOps Bootcamp lesson?

Yes. Every DevOps Bootcamp lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Records, Fields, and Custom Separators in awk
  2. Patterns, Ranges, and BEGIN/END Blocks
  3. Aggregation with awk Arrays and Grouping
  4. awk Functions, printf Formatting, and Report Generation
← Back to DevOps Bootcamp