Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 59

Awk for Structured Data Extraction

Recommended resources

Hello! Welcome to the next lesson in your Bash scripting journey.

In our last session, you learned how to use sed to perform powerful find-and-replace operations on text streams. This is perfect for editing configuration files or cleaning up log data. Today, we'll dive into sed's powerful companion: awk.

While sed is line-oriented, awk is field-oriented, or column-aware. It excels at processing structured text where data is organized into columns, such as log files, CSVs, or the output of many system commands. Think of it as a programmable spreadsheet for your terminal.

Today's Learning Outcome: By the end of this lesson, you will be able to extract and format structured data from text files using awk.


1. What is awk and How Does It Work?

awk was created by Alfred Aho, Peter Weinberger, and Brian Kernighan, and its name is derived from their last names. It processes input (from a file or a pipe) one line at a time. For each line, it performs three main steps automatically:

  1. It splits the line into "fields" based on a delimiter (by default, any whitespace).
  2. It checks if the line matches a specified pattern or condition.
  3. If it matches, it executes a corresponding action.

The basic syntax you'll use constantly is:

awk 'pattern { action }' input_file

  • pattern: A condition to filter lines. If omitted, the action applies to every line.
  • action: The commands to execute on matched lines, enclosed in curly braces {}. If omitted, the default action is to print the entire line.

To get started, let's create a sample file. In your terminal, run this command to create servers.txt:

cat > servers.txt << 'EOF'
web-01 us-east-1 4
db-01 eu-west-2 8
api-01 us-east-1 16
cache-01 us-east-1 2
web-02 eu-west-2 4
EOF

This file contains a list of servers, their region, and the number of CPU cores.

Now, let's read about the core concepts of awk and how to access these columns of data.

Introduction to awk for Text Processing and Data Extraction

This article provides a fantastic introduction to awk. We'll start with the sections that cover its basic syntax and how it handles fields.

Read the sections 'What is awk?', 'Part 1: Basic awk Syntax', and 'Part 2: Working with Fields'. Focus on understanding: The concept of fields: $0 (the whole line), $1 (the first field), $2, etc. The special variable $NF, which always refers to the last field on a line.

As you've read, you can easily extract specific columns. For example, to print just the server names (field 1) and their CPU cores (field 3) from our servers.txt file, you would run:

awk '{print $1, $3}' servers.txt

Output:

web-01 4
db-01 8
api-01 16
cache-01 2
web-02 4


2. Filtering Data and Using Built-in Variables

awk becomes truly powerful when you combine field extraction with filtering. You can tell awk to process only the lines that meet certain criteria. Furthermore, awk provides several built-in variables that give you more context about the data you're processing.

Key built-in variables include:

  • NR: The current Number of Records (line number).
  • NF: The Number of Fields in the current line.
  • FS: The Field Separator. By default, this is whitespace, but you can change it to handle other formats, like comma-separated (CSV) or colon-separated files.

The most common way to change the field separator is with the -F command-line flag. For example, to process /etc/passwd, which uses a colon (:) as a delimiter, you would use awk -F':' ....

Let's dive deeper into these variables and how to build filtering patterns.

Introduction to awk for Text Processing and Data Extraction

These next sections of the article explain how to use built-in variables and construct patterns to filter your data.

Read 'Part 3: Built-in Variables' and 'Part 4: Patterns and Conditions'. Pay close attention to: How to change the field separator with -F. Using comparison operators (e.g., $3 > 8) and string comparison ($2 == "us-east-1"). Note that strings must be in double quotes inside the awk script. Combining conditions with logical operators (&& for AND, || for OR).

For instance, to find all servers in our file that have more than 4 CPU cores, you can use a pattern:

awk '$3 > 4 {print $1, "has", $3, "cores"}' servers.txt

Output:

db-01 has 8 cores
api-01 has 16 cores
Test your understanding!
  1. How would you use awk to display just the username (field 1) and login shell (field 7) for all users in the /etc/passwd file?
  2. Using servers.txt, how would you print the names of all servers located in the eu-west-2 region?
  3. How would you modify the previous command to also show the line number for each server found?
Show answer
  1. Since /etc/passwd is colon-delimited, you need the -F flag:
    awk -F':' '{print $1, $7}' /etc/passwd

  2. Use a string comparison pattern:
    awk '$2 == "eu-west-2" {print $1}' servers.txt

  3. Use the NR variable in the action block:
    awk '$2 == "eu-west-2" {print NR, $1}' servers.txt



3. Calculations and Formatted Reports

One of awk's most powerful features is its ability to perform calculations and generate formatted reports. This is where the special BEGIN and END patterns come in handy.

  • BEGIN { action }: This block executes once before any lines are read. It's perfect for initializing variables or printing a report header.
  • END { action }: This block executes once after all lines have been processed. It's ideal for printing summaries, totals, or averages.

While print is good for simple output, the printf function gives you C-style control over formatting, allowing you to create clean, aligned columns and control numerical precision. This is essential for generating human-readable reports.

Introduction to awk for Text Processing and Data Extraction

Now, let's explore how to use awk for calculations and creating well-formatted reports.

Please read 'Part 5: BEGIN and END Blocks', 'Part 6: Calculations and Arithmetic', and 'Part 7: Formatting Output'. Focus on: The purpose of BEGIN and END. How to declare and update variables to calculate sums and averages. The syntax of printf and common format specifiers like %-10s (left-aligned string) and %.2f (float with 2 decimal places).

Let's combine these concepts to create a summary report from our servers.txt file. This command will print a formatted table and calculate the total number of CPU cores.

awk '
  BEGIN {
    total_cores = 0
    printf "%-15s %-15s %s\n", "Server", "Region", "Cores"
    print "---------------------------------------"
  }
  {
    printf "%-15s %-15s %d\n", $1, $2, $3
    total_cores += $3
  }
  END {
    print "---------------------------------------"
    printf "Total Cores: %d\n", total_cores
  }
' servers.txt

Output:

Server          Region          Cores
---------------------------------------
web-01          us-east-1       4
db-01           eu-west-2       8
api-01          us-east-1       16
cache-01        us-east-1       2
web-02          eu-west-2       4
---------------------------------------
Total Cores: 34


4. Advanced Data Aggregation with Arrays

For more complex reporting, awk supports associative arrays, which are similar to dictionaries in Python or associative arrays in Bash. You can use them to group and aggregate data in powerful ways. A common DevOps task is to count occurrences of different items, such as counting servers per region or errors by type in a log file.

Introduction to awk for Text Processing and Data Extraction

This final reading introduces one of awk's most advanced features: associative arrays. This is key for creating summary reports.

Read 'Part 10: Arrays in awk'. Focus on the 'Count Occurrences' and 'Group Data' examples. Understand the array[key]++ idiom for counting.

Using our servers.txt file, we can use an array to count how many servers we have in each region:

awk '{ regions[$2]++ } END { for (r in regions) print r, regions[r] }' servers.txt

Output:

us-east-1 3
eu-west-2 2

Here's how it works:

  1. regions[$2]++: For each line, it uses the region (field 2) as a key in the regions array and increments its value. If the key doesn't exist, awk creates it with a value of 1.
  2. END { for (r in regions) ... }: After processing all lines, it loops through all the keys (r) in the regions array and prints each key along with its final count.


Conclusion

Congratulations! You've now learned the fundamentals of awk, a cornerstone of command-line data processing. You've moved beyond simple line editing with sed and can now parse, filter, calculate, and format structured data—a skill you will use daily as a DevOps engineer for analyzing logs, monitoring system metrics, and generating reports.

Key Takeaways:

  • awk is a field-oriented text processor ideal for structured, column-based data.
  • The core structure is pattern { action }, which is applied to each line of input.
  • Data is accessed via field variables: $1, $2, $NF, etc. The delimiter can be changed with -F.
  • BEGIN and END blocks are used for setup (like printing headers) and teardown (like printing totals).
  • awk can perform arithmetic and uses printf for creating well-formatted, tabular reports.
  • Associative arrays allow for powerful data aggregation, like grouping and counting.

In our next lesson, we will shift focus to making our scripts more robust. You'll learn how to implement error handling using exit codes and trap commands, ensuring your automation scripts behave predictably and fail gracefully.