Text Processing with Regular Expressions

LinuxBeginner
Practice Now

Introduction

A regular expression describes text by structure rather than one exact string. The same pattern language appears in many Linux tools, but each tool uses it differently: grep selects or extracts matches, sed transforms a stream, and awk combines patterns with field-aware actions.

You will start with anchors and character classes, move to extended repetition and extraction, preview transformations before saving them, and finish with field selection and aggregation. Patterns are quoted so the shell passes special characters to the text tool instead of interpreting them itself.

Match Lines with Basic Regex Structure

In this step, you will use literal text, anchors, and character classes to describe whole-line positions.

Inspect the practice file:

cd /home/labex/project/regex-lab
nl -ba contacts.txt

Literal text matches anywhere in a line:

grep 'active' contacts.txt

^ anchors a pattern to the beginning and $ anchors it to the end. Find the Alice record and records ending in a two-digit number:

grep '^Alice|' contacts.txt
grep '[0-9][0-9]$' contacts.txt

Inside brackets, a character class matches one character. [ABC] means A, B, or C:

grep '^[ABC]' contacts.txt | tee abc-contacts.txt

This produces Alice, Bob, and Carol. Save anchored numeric matches too:

grep '[0-9][0-9]$' contacts.txt > two-digit-values.txt

Use Extended Regex and Extract Matches

In this step, you will use extended regular expressions for alternatives and repetition, then extract only the matching substring.

grep -E enables operators such as + (one or more), ? (zero or one), and | (alternative). Match complete active or pending status fields:

cd /home/labex/project/regex-lab
grep -E '\|(active|pending)\|' contacts.txt

An email-like pattern can be built in pieces:

  • [[:alnum:]._-]+ matches one or more allowed local-part characters;
  • @ is literal;
  • [[:alnum:].-]+ matches the domain body;
  • \.[[:alpha:]]{2,} matches a dot and a two-or-more-letter suffix.

Use -o to print only matching substrings:

grep -Eo '[[:alnum:]._-]+@[[:alnum:].-]+\.[[:alpha:]]{2,}' contacts.txt | tee emails.txt

Number matching lines and count extracted addresses:

grep -En '\|(active|pending)\|' contacts.txt
wc -l < emails.txt | tr -d ' ' > email-count.txt
cat email-count.txt

The malformed dave_at_example.com does not match because it has no @.

Preview Transformations with Sed

In this step, you will transform a stream with sed while leaving the input unchanged.

A substitution has the form s/pattern/replacement/flags. Replace only the first a on each line, then every lowercase a using the g flag:

cd /home/labex/project/regex-lab
sed 's/a/A/' contacts.txt
sed 's/a/A/g' contacts.txt

Use a different delimiter when the text itself contains slashes or when another delimiter is clearer. Replace email domains with redacted.invalid:

sed -E 's#@[[:alnum:].-]+\.[[:alpha:]]{2,}#@redacted.invalid#g' contacts.txt

Sed addresses select which lines receive a command. Apply a status replacement only to lines containing an exact |inactive| field:

sed '/|inactive|/s/|inactive|/|disabled|/' contacts.txt | tee contacts-preview.txt

Confirm that the original still contains inactive:

grep '|inactive|' contacts.txt

Create a Cleaned File with Sed

In this step, you will combine multiple sed commands and save the transformed stream as a new file.

The header in services.csv should remain unchanged. For data rows, normalize warn to warning and add a ms suffix to the final numeric field. -e supplies multiple commands, and the address 2,$ limits them to lines 2 through the end:

cd /home/labex/project/regex-lab
sed -E -e '2,$ s/,warn,/,warning,/' -e '2,$ s/([0-9]+)$/\1ms/' services.csv | tee services-clean.csv

Here, parentheses capture the final digits and \1 reuses that capture in the replacement.

Compare the original and new files:

diff -u services.csv services-clean.csv || true

The nonzero diff status is expected because the output intentionally changed; || true allows a copied command sequence to continue.

Select Fields and Aggregate with Awk

In this step, you will use awk when a task needs both field structure and calculations.

-F, sets comma as the input separator. Print service and status for data rows only; NR is the current record number:

cd /home/labex/project/regex-lab
awk -F, 'NR > 1 {print $1, $2}' services.csv

Select rows whose numeric third field exceeds 200:

awk -F, 'NR > 1 && $3 > 200 {print $1, $3}' services.csv | tee slow-services.txt

Accumulate a total and count, then calculate an average in the END block. printf controls the numeric format:

awk -F, 'NR > 1 {sum += $3; count++} END {printf "average_latency=%.1f\n", sum/count}' services.csv | tee latency-summary.txt

Awk patterns decide which records run an action; fields such as $1 and $3 make structured calculations readable.

Summary

You built regex patterns from anchors, classes, alternatives, and repetition; used grep to select and extract; used sed to preview and save transformations; and used awk for field-aware selection and aggregation. The next Challenge asks you to apply these ideas without being given a full command sequence.