Section 1 of 16

1 Before you start

Before you begin, take a few minutes to check what you already know and how confident you feel. You will see the same questions again at the end of the module — this helps both you and us measure what you have learned. Click an option for every question and confidence rating, then click Next to continue.

Pre-test

What does sed 's/foo/bar/' do to a line?

Pre-test

How do you make sed replace EVERY match on the line, not just the first?

Pre-test

In awk, what does $0 mean?

Pre-test

Which awk one-liner sums column 6 (systolic) of a comma-separated cohort file, skipping the header?

Pre-test

Which flag tells awk that the field separator is a comma?

Pre-test

In awk, what does NR contain?

Pre-test

Which awk program prints the THIRD field of every line?

Pre-test

What does the -i flag do in sed -i 's/draft/final/g' report.txt?

Pre-confidence

I can do a search-and-replace on a file with sed.

Not at all confident
Fully confident
Pre-confidence

I can use awk to select specific columns from a delimited file.

Not at all confident
Fully confident
Pre-confidence

I can filter rows in awk based on a numeric condition.

Not at all confident
Fully confident
Section 2 of 16

2 Introduction

Two more tools for your text-wrangling toolkit. sed is the stream editor - it transforms text as it flows by, mostly via search-and-replace. awk is a tiny programming language purpose-built for processing tabular data line by line and column by column. Together they handle the 80% of data-cleaning jobs that grep, cut, sort, and uniq cannot quite reach. We will use a patient vitals CSV so the patterns transfer straight to clinical data work.

In this lesson, we will look at the following:

  • sed for search-and-replace and line editing.
  • awk's mental model: pattern { action } per line.
  • Selecting and computing on columns with $1, $2, ... $NF.
  • Filtering rows by a condition; aggregating with END { ... }.
sed and awk both process text one line at a time, sed rewriting the text of each line while awk splits each line into columns ($1 to $NF) to test a condition and an END block aggregates a result after the final line.
sed and awk both process text one line at a time, sed rewriting the text of each line while awk splits each line into columns ($1 to $NF) to test a condition and an END block aggregates a result after the final line.
Section 3 of 16

3 sed - Stream Editor

sed is a stream editor. It reads text flowing through your terminal (a stream), automatically applies rules to modify it on the fly, and spits out the result.

It is incredibly powerful for automating edits across massive files or cleaning up data in a pipeline.

sed processes text one line at a time, applying its substitution rule to each line as it flows through, so edits happen continuously on a stream rather than on a whole file at once.
sed processes text one line at a time, applying its substitution rule to each line as it flows through, so edits happen continuously on a stream rather than on a whole file at once.
Section 3.1 of 16

3.1 Substitution - the main use case

The most common use for sed is searching and replacing text.

The sed ‘s/SEARCH_PATTERN/REPLACEMENT_TEXT/FLAGS’ form is the syntax for the search-and-replace.

$ echo 'Hello world' | sed 's/world/bash/'
Hello bash
The sed s command performs a search-and-replace using the form s/pattern/replacement/, so s/world/bash/ turns Hello world into Hello bash.
The sed s command performs a search-and-replace using the form s/pattern/replacement/, so s/world/bash/ turns Hello world into Hello bash.

If you want to replace every instance on a line, add the g (global) flag at the end.

$ echo 'aaa bbb aaa' | sed 's/aaa/zzz/'
zzz bbb aaa
$ echo 'aaa bbb aaa' | sed 's/aaa/zzz/g'
zzz bbb zzz
A sed substitution replaces only the first match on each line by default, and adding the g flag makes it replace every match.
A sed substitution replaces only the first match on each line by default, and adding the g flag makes it replace every match.
Try it out

Try this snippet in the Bash Scratchpad on the right.

Try this snippet
$ head -n 2 cohort_2026.csv | sed 's/,/ | /'
$ head -n 2 cohort_2026.csv | sed 's/,/ | /g'
Section 3.2 of 16

3.2 Editing a file

By default, sed just prints the modified text to your screen; it does not change the original file.

To save your changes, you have two options

$ sed 's/draft/final/g' report.txt > report_final.txt

Or you can use the -i flag. The -i flag tells sed to overwrite the original file.

$ sed -i 's/draft/final/g' report.txt

You can tell sed to edit in-place but keep a backup of the original file by adding an extension directly after -i:

$ sed -i.bak 's/draft/final/g' report.txt
sed prints to the screen by default and leaves the file untouched; redirecting with > writes the result to a new file, while -i overwrites report.txt in place and -i.bak overwrites it but keeps the original as report.txt.bak.
sed prints to the screen by default and leaves the file untouched; redirecting with > writes the result to a new file, while -i overwrites report.txt in place and -i.bak overwrites it but keeps the original as report.txt.bak.
Section 3.3 of 16

3.3 A different delimiter when / is awkward

If you are trying to replace file paths or URLs, using the standard / delimiter gets incredibly messy because you have to "escape" every slash with a backslash \. (Programmers call this "leaning toothpick syndrome").

You might end up with something messy like this

$ echo '/home/old/data' | sed 's/\/home\/old/\/home\/new/'

Instead, sed allows you to use almost any character as a delimiter if you place it right after the s. Using the pipe | or a colon : makes paths much easier to read!

$ echo '/home/old/data' | sed 's|/home/old|/home/new|'
/home/new/data
sed treats whatever character follows the s as the substitution delimiter, so swapping the default / for | or : lets you rewrite file paths without backslash-escaping every slash.
sed treats whatever character follows the s as the substitution delimiter, so swapping the default / for | or : lets you rewrite file paths without backslash-escaping every slash.
Section 3.4 of 16

3.4 Deleting lines

sed isn't just for replacing text; it's great for quickly removing rows of data using d, which is highly useful when cleaning up CSVs or config files.

You can delete a specific line number:

$ sed '1d' cohort_2026.csv

or a range of lines.

$ sed '5,10d' cohort_2026.csv

You can use regular expressions to delete lines.

$ sed '/^CTRL/d' cohort_2026.csv
sed deletes lines by pairing the d command with an address, whether a line number, a range like 5,10, or a /regex/ pattern, which makes it a fast way to strip headers, blocks, or matching rows out of CSV and config files.
sed deletes lines by pairing the d command with an address, whether a line number, a range like 5,10, or a /regex/ pattern, which makes it a fast way to strip headers, blocks, or matching rows out of CSV and config files.
The twelve POSIX extended regular expression metacharacters Bash recognises through its =~ operator, each paired with a minimal pattern and a string it matches.
The twelve POSIX extended regular expression metacharacters Bash recognises through its =~ operator, each paired with a minimal pattern and a string it matches.

What if you want to make two different substitutions at the same time? You can chain commands together using the -e (expression) flag.

$ sed -e 's/Positive/1/g' -e 's/Negative/0/g' biomarker_panel.csv
Multiple -e expressions stack into a single sed command so several substitutions apply in one line-by-line pass over a file.
Multiple -e expressions stack into a single sed command so several substitutions apply in one line-by-line pass over a file.
Section 4 of 16

4 awk - Line-and-Column Processing

While tools like sed are great for replacing text, awk is an entire programming language built explicitly for working with structured data, like CSVs or log files. If your data has rows and columns, awk is your best friend.

Section 4.1 of 16

4.1 The mental model

awk reads input one line at a time. For each line it checks a pattern and runs an action when the pattern matches:

awk 'PATTERN { ACTION }' file

Inside the action block, awk automatically splits the line into variables for you:

  • $1, $2, $3... represent the first, second, third columns (fields) of the line.
  • $0 represents the entire line.
  • NF (Number of Fields) tells you how many columns are in the current line.
  • NR (Number of Records) tells you the current row/line number.

By default, awk assumes columns are separated by spaces or tabs. If you are reading a CSV, use -F, to set the field separator to a comma.

awk processes a file one line at a time and automatically splits each line into numbered fields, where $1, $2, $3... are the columns, $0 is the entire line, NF is the number of fields, and NR is the current line number.
awk processes a file one line at a time and automatically splits each line into numbered fields, where $1, $2, $3... are the columns, $0 is the entire line, NF is the number of fields, and NR is the current line number.
Section 4.2 of 16

4.2 Print a column

If you don't provide a pattern, awk assumes you want to match every line. Let's look at a sample file, cohort_2026.csv:

$ cat cohort_2026.csv
patient_id,age,sex,height_cm,weight_kg,systolic,diastolic
P-001,54,F,168,72,138,84
P-002,61,M,172,88,152,95
P-003,49,F,159,55,118,72

To print just the patient_id (Column 1):

$ awk -F, '{ print $1 }' cohort_2026.csv
patient_id
P-001
P-002
P-003

To print multiple columns, separate them with commas in your print statement. awk will insert a space between them in the output:

$ awk -F, '{ print $1, $6 }' cohort_2026.csv
patient_id systolic
P-001 138
P-002 152
P-003 118
awk splits each row on its field separator into numbered fields, and you select columns by referring to them as $1, $2, $3 and so on.
awk splits each row on its field separator into numbered fields, and you select columns by referring to them as $1, $2, $3 and so on.
Try it out

Try this snippet in the Bash Scratchpad on the right.

Try this snippet
$ awk -F, '{ print $1, $6 }' cohort_2026.csv | head -n 4
Section 4.3 of 16

4.3 Filter rows by a condition

You can use standard logical operators (==, !=, <, >, &&, ||) in your pattern to filter rows.

For example, let's find patients with a systolic blood pressure (Column 6) of 140 or higher.

$ awk -F, '$6 >= 140 { print $1, $6, $7 }' cohort_2026.csv
patient_id systolic diastolic
P-002 152 95

To safely skip the header, tell awk to only look at rows where the Number of Records (NR) is greater than 1:

$ awk -F, 'NR > 1 && $6 >= 140 { print $1 }' cohort_2026.csv
P-002
An awk pattern is a true or false test applied to every line, so NR > 1 && $6 >= 140 skips the header and keeps only data rows whose sixth field is at least 140, running print $1 on each match.
An awk pattern is a true or false test applied to every line, so NR > 1 && $6 >= 140 skips the header and keeps only data rows whose sixth field is at least 140, running print $1 on each match.

Hypertension is usually defined as systolic >= 140 OR diastolic >= 90. We can combine conditions using || (OR) and format the output strings directly:

$ awk -F, 'NR > 1 && ($6 >= 140 || $7 >= 90) { print $1, $6"/"$7 }' cohort_2026.csv
P-002 152/95
The || operator combines two conditions so a record is kept whenever at least one is true, which is why a patient over either the systolic or the diastolic threshold counts as hypertensive.
The || operator combines two conditions so a record is kept whenever at least one is true, which is why a patient over either the systolic or the diastolic threshold counts as hypertensive.
Try it out

Try this snippet in the Bash Scratchpad on the right.

Try this snippet
$ awk -F, 'NR > 1 && $6 >= 140 { print $1, $6, $7 }' cohort_2026.csv | head
Section 4.4 of 16

4.4 Aggregation with END

awk has two special patterns: BEGIN (runs once before reading the file) and END (runs once after the last line). END is perfect for computing totals and averages.

Also, awk variables don't need to be declared; they default to 0.

$ awk -F, 'NR > 1 { total += $6; n++ } END { print "mean systolic:", total/n }' cohort_2026.csv
mean systolic: 136.25

For every line (except the header), it adds the systolic value ($6) to a variable called total and increments a counter n. At the END, it divides them.

In awk, undeclared variables default to 0, the per-line block runs once for each data line to accumulate a sum and a count, and the END block runs a single time after the last line to turn that running total into a mean.
In awk, undeclared variables default to 0, the per-line block runs once for each data line to accumulate a sum and a count, and the END block runs a single time after the last line to turn that running total into a mean.
Try it out

Try this snippet in the Bash Scratchpad on the right.

Try this snippet
$ awk -F, 'NR > 1 { s += $6 } END { print "mean systolic:", s/(NR-1) }' cohort_2026.csv
Section 4.5 of 16

4.5 Group-by aggregation

awk has built-in associative arrays (like dictionaries in Python), allowing you to group data. Let's find the mean systolic pressure, grouped by sex (Column 3):

$ awk -F, 'NR > 1 { sum[$3] += $6; n[$3]++ } END { for (s in sum) print s, sum[s]/n[s] }' cohort_2026.csv
F 132.4
M 142.8

sum[$3] creates an array where the "key" is the sex (F or M). We sum the blood pressures and count the occurrences for each key independently. In the END block, we loop through the array and print the averages. It's a spreadsheet pivot table in a one-liner.

awk uses a field value as the key into associative arrays, so one pass over the data accumulates a running sum and count per group and the END block divides them to produce a per-group summary such as mean systolic pressure by sex.
awk uses a field value as the key into associative arrays, so one pass over the data accumulates a running sum and count per group and the END block divides them to produce a per-group summary such as mean systolic pressure by sex.
Section 5 of 16

5 When to Use What

The command line has many text tools. Knowing which one to pick saves you massive amounts of time:

  • Just want to FIND lines? grep is enough.
  • Need fixed COLUMNS from a clean delimited file? cut.
  • Need a SEARCH AND REPLACE? sed.
  • Need to FILTER rows by a numeric condition or compute on columns? awk.
Choosing among grep, cut, sed and awk by matching each tool to the job it does best, finding lines, extracting fixed columns, replacing text, and filtering rows by a numeric test or computing on columns.
Choosing among grep, cut, sed and awk by matching each tool to the job it does best, finding lines, extracting fixed columns, replacing text, and filtering rows by a numeric test or computing on columns.
Section 6 of 16

6 Check your understanding

You have reached the end of the module. Try the same questions again — your answers here, paired with your pre-test answers, are how we measure what the module taught you. Answer every question and confidence rating, then click Submit and see results to view your score.

Post-test

What does sed 's/foo/bar/' do to a line?

Post-test

How do you make sed replace EVERY match on the line, not just the first?

Post-test

In awk, what does $0 mean?

Post-test

Which awk one-liner sums column 6 (systolic) of a comma-separated cohort file, skipping the header?

Post-test

Which flag tells awk that the field separator is a comma?

Post-test

In awk, what does NR contain?

Post-test

Which awk program prints the THIRD field of every line?

Post-test

What does the -i flag do in sed -i 's/draft/final/g' report.txt?

Post-confidence

I can do a search-and-replace on a file with sed.

Not at all confident
Fully confident
Post-confidence

I can use awk to select specific columns from a delimited file.

Not at all confident
Fully confident
Post-confidence

I can filter rows in awk based on a numeric condition.

Not at all confident
Fully confident
Section 7 of 16

7 Your results

Here is how your post-test answers compare with your pre-test answers. The pre/post pairing is the most reliable way to see what this module actually taught you.

Your score

Submit the post-test to see your results.

Muddiest point

What is the one thing from this module that is still unclear to you?

Rate this module

Overall, how would you rate this module?

How likely are you to recommend this module to a peer? (0 = not at all, 10 = extremely likely)