Replace Mutliple Lines Using Linux awk: Multiline Processing

In this tutorial, you’ll learn how to replace multiple lines using awk in Linux.

We will cover how to replace multiple lines, handle nested blocks, work with patterns, and more.

 

 

Replace Multiple Lines with a Single Line

First, let’s consider a sample file customer_data.txt with the following content:

CustomerID: 001
Name: Alex
Plan: Basic

CustomerID: 002
Name: Morgan
Plan: Premium

Suppose you want to replace the details of each customer which span multiple lines with a single line summary.

Here’s the awk command to do that:

awk 'BEGIN{RS=""; FS="\n"} {print $1", "$2", "$3}' customer_data.txt

Output:

CustomerID: 001, Name: Alex, Plan: Basic
CustomerID: 002, Name: Morgan, Plan: Premium

This command sets the record separator RS to an empty string and to treat consecutive lines as one record, and the field separator FS to a newline.

 

Replace Multiple Lines with Multiple Lines

Let’s say you want to replace each customer’s details with a more detailed format.

Here’s the awk command to do this:

awk 'BEGIN{RS=""; FS="\n"} {print $1"\n"$2"\n"$3"\nStatus: Active\nPoints: "($3=="Plan: Premium"?250:100)}' customer_data.txt

Output:

CustomerID: 001
Name: Alex
Plan: Basic
Status: Active
Points: 100
CustomerID: 002
Name: Morgan
Plan: Premium
Status: Active
Points: 250

This command treats the file as a single record, splits it into fields based on newline characters, and prints the first three fields followed by “Status: Active” and “Points: X” based on the value of the third field.

If the third field is “Plan: Premium”, it will be 250, otherwise, it will be 100.

 

Replace Multiple Lines Based on a Condition

Let’s assume customers with more than 200 points should be upgraded to a “Gold” plan.

Original customer_data.txt content:

CustomerID: 001
Name: Alex
Plan: Basic
Points: 100

CustomerID: 002
Name: Morgan
Plan: Premium
Points: 250

To update the plan to “Gold” for customers with points greater than 200, you can use regular expressions to check if the points are greater than 200 ([2-9][0-9][0-9]):

awk 'BEGIN{RS=""; FS="\n"} {if ($4 ~ /Points: [2-9][0-9][0-9]/) $3="Plan: Gold"; print $1"\n"$2"\n"$3"\n"$4"\n"}' customer_data.txt

Output:

CustomerID: 001
Name: Alex
Plan: Basic
Points: 100

CustomerID: 002
Name: Morgan
Plan: Gold
Points: 250

If the condition is met, the plan is changed to “Gold”. Then, it prints the modified details for each customer.

 

Replace Based on Line Numbers

Suppose you want to update the lines 5 to 7.

Original customer_data.txt content:

CustomerID: 001
Name: Alex
Plan: Basic
Points: 100

CustomerID: 002
Name: Morgan
Plan: Premium
Points: 250

Here’s how it can be done with awk:

awk 'NR==5,NR==7 {if (NR==5) print "CustomerID: 003"; else if (NR==6) print "Name: Taylor"; else if (NR==7) print "Plan: Standard"; next} 1' customer_data.txt

Output:

CustomerID: 001
Name: Alex
Plan: Basic
Points: 100
CustomerID: 003
Name: Taylor
Plan: Standard
Points: 250

In this awk command, NR==5,NR==7 selects the line number range. For each line in this range, it prints the new information.

The next command skips the original line, and the 1 at the end prints all lines not in the specified range.

 

Pattern Spread Over Multiple Lines

Original customer_data.txt content:

CustomerID: 001
Name: Alex
Plan: Basic
Points: 100

CustomerID: 002
Name: Morgan
Plan: Premium
Points: 250

You want to replace the entire block for customers with “Plan: Premium” and “Points: 250” with “Membership: Suspended”. Here’s the awk command:

awk 'BEGIN{RS=""; FS="\n"} /Plan: Premium\nPoints: 2[5-9][0-9]/ {print "Membership: Suspended\n"; next} {print $0"\n"}' customer_data.txt

Output:

CustomerID: 001
Name: Alex
Plan: Basic
Points: 100

Membership: Suspended

In this command, awk is set to treat each customer’s details as a single record.

It searches for records matching the pattern “Plan: Premium” and “Points” between 250 and 299.

If the pattern is found, it replaces the entire block with “Membership: Suspended”.

 

Replace a Range of Lines with Lines from Another File

Imagine you have two files: customer_data.txt (our main file) and new_entries.txt (a file with new customer data).

You want to replace lines 6 to 9 in customer_data.txt with the content from new_entries.txt.

Original customer_data.txt content:

CustomerID: 001
Name: Alex
Plan: Basic
Points: 100

CustomerID: 002
Name: Morgan
Plan: Premium
Points: 250

new_entries.txt content:

CustomerID: 004
Name: Jordan
Plan: Standard

Here’s the command to do that:

awk 'NR==6,NR==9{if(!f){system("cat new_entries.txt"); f=1; next} next} 1' customer_data.txt

Output:

CustomerID: 001
Name: Alex
Plan: Basic
Points: 100

CustomerID: 004
Name: Jordan
Plan: Standard

In this command, awk is used to identify the range of lines (6 to 9) in customer_data.txt.

When this range is reached, awk executes the system("cat new_entries.txt") command which prints the content of new_entries.txt and replaces the specified range.

The next command skips the original lines in the range, and the 1 at the end prints all other lines.

 

Replace Multiple Lines Using a Variable

Suppose you have another file update_info.txt that contains updated information that needs to replace a specific block of text in customer_data.txt.

Original customer_data.txt content:

CustomerID: 001
Name: Alex
Plan: Basic
Points: 100

CustomerID: 002
Name: Morgan
Plan: Premium
Points: 250

update_info.txt contains:

CustomerID: 002
Name: Jordan
Plan: Gold
Points: 300

You want to replace the details of CustomerID: 002 in customer_data.txt with the content of update_info.txt.

First, read the contents of update_info.txt into a variable:

update=$(<update_info.txt)

Now, use awk to replace the lines:

awk -v var="$update" 'BEGIN{RS=""; FS="\n"} /CustomerID: 002/ {print var; next} {print $0"\n"}' customer_data.txt

Output:

CustomerID: 001
Name: Alex
Plan: Basic
Points: 100

CustomerID: 002
Name: Jordan
Plan: Gold
Points: 300

In this awk command, the -v option is used to pass the variable update containing the new customer details.

awk then searches for the record containing “CustomerID: 002” and replaces it with the content of update variable.

 

Nested Block Replacement

Original customer_data.txt content:

CustomerID: 001
Name: Alex
Plan: Basic
Billing:
  Last Payment: $50
  Due Date: 2024-04-05
Contact:
  Email: alex@example.com
  Phone: 123-456-7890

CustomerID: 002
Name: Morgan
Plan: Premium
Billing:
  Last Payment: $100
  Due Date: 2024-04-10
Contact:
  Email: morgan@example.com
  Phone: 987-654-3210

Suppose you need to update the nested billing details for “Plan: Premium” customers and the contact details for “CustomerID: 001”.

Here’s an awk command that can do this:

awk '
BEGIN { RS=""; FS="\n"; OFS="\n" }
/CustomerID: 001/ {
    for (i=1; i<=NF; i++) {
        if ($i ~ /Contact:/) { inContactBlock = 1 }
        if (inContactBlock) {
            if ($i ~ /Email:/) $i = "  Email: alex_new@example.com"
            if ($i ~ /Phone:/) { $i = "  Phone: 111-222-3333"; inContactBlock = 0 }
        }
    }
}
/Plan: Premium/ {
    for (i=1; i<=NF; i++) {
        if ($i ~ /Billing:/) { inBillingBlock = 1 }
        if (inBillingBlock) {
            if ($i ~ /Last Payment:/) $i = "  Last Payment: $150"
            if ($i ~ /Due Date:/) { $i = "  Due Date: 2024-05-10"; inBillingBlock = 0 }
        }
    }
}
{ print $0"\n" }
' customer_data.txt

Output:

CustomerID: 001
Name: Alex
Plan: Basic
Billing:
  Last Payment: $50
  Due Date: 2024-04-05
Contact:
  Email: alex_new@example.com
  Phone: 111-222-3333

CustomerID: 002
Name: Morgan
Plan: Premium
Billing:
  Last Payment: $150
  Due Date: 2024-05-10
Contact:
  Email: morgan@example.com
  Phone: 987-654-3210

This awk script uses variables to track if it’s currently within the relevant blocks (Contact or Billing) and applies the necessary updates when criteria are met.

Leave a Reply

Your email address will not be published. Required fields are marked *