Skip to content
VLSI Mentor

sed Part II · Module 3 · Substitution and Replacement

Basic Search and Replace

This is the command you came for. Part I explained how sed works; Part II teaches what to type, and it starts with the substitute command — the one that does ninety per cent of real sed work. The command itself is small enough to learn in a minute. What takes longer is its three defaults, because each one is the opposite of what people assume: it changes only the first match on a line, it changes only sed's buffer, and it never writes to your file. This chapter shows all three on real filelists, and ends with the two habits that let you tell a successful edit from a command that quietly did nothing.

Foundation16 min readsedPart IISubstitutionFundamentalsFilelists

Part II · Module 3 · Chapter 3.1 · Basic Search and Replace

1. What This Command Is For

You have a file. Some text in it is wrong, and the correction is mechanical — a path that moved, a flag that was renamed, a define that should now say something else. You know exactly what to look for and exactly what to put in its place.

That is the substitute command, and it is written s. It is the most-used command in sed by a wide margin, and almost everything else in this track either feeds it better patterns or controls which lines it runs on.

We will build it up slowly: the shape first, then the simplest possible run, then each of the three defaults that trip people up.

2. The Four Parts

A substitution has four parts. Three of them are yours; the fourth is the character that separates them.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
s / axi_awvalid / m_axi_awvalid /
│ │      │              │       │
│ │      │              │       └─ closing delimiter
│ │      │              └───────── replacement: what to put there
│ │      └──────────────────────── pattern: what to look for
│ └─────────────────────────────── delimiter: any character you choose
└───────────────────────────────── the substitute command

Read it as a sentence: substitute axi_awvalid with m_axi_awvalid.

The delimiter is conventionally /, which is why people say "s slash". But it is genuinely your choice of character — and when the text you are editing contains slashes, choosing a different one saves a great deal of pain. Chapter 3.3 covers that properly; this chapter uses / except where a path forces otherwise.

Two things are not in the command: there is no "which occurrence" and no "which file". Those are the defaults, and they are the subject of the next few sections.

3. The Simplest Run

Start with no file at all, just a line typed in through a pipe:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
printf 'hello world\n' | sed 's/world/VLSI/'
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
hello VLSI

That is the whole idea. Now something an engineer would actually do. Here is a compile filelist:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
cat -n filelists/top.f
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
     1	+define+DEBUG
     2	+incdir+/proj/old/rtl
     3	/proj/old/rtl/alu.sv
     4	/proj/old/rtl/fifo.sv
     5	/proj/old/tb/env.sv

The build should now be a release build, so +define+DEBUG has to become +define+RELEASE:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sed 's/+define+DEBUG/+define+RELEASE/' filelists/top.f
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
+define+RELEASE
+incdir+/proj/old/rtl
/proj/old/rtl/alu.sv
/proj/old/rtl/fifo.sv
/proj/old/tb/env.sv

One line changed; the other four came through untouched. That is the whole file printed to your terminal, which Chapter 2.2 explained is auto-print doing its job — lines that match nothing still get printed.

You can often type less. Since only one line contains DEBUG, the pattern can be just that word:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sed 's/DEBUG/RELEASE/' filelists/top.f
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
+define+RELEASE
+incdir+/proj/old/rtl
/proj/old/rtl/alu.sv
/proj/old/rtl/fifo.sv
/proj/old/tb/env.sv

Same result, less typing. Whether that is a good idea depends entirely on whether DEBUG appears anywhere else in the file — it does not here, but in a 400-line filelist you would want to check first. Being more specific than you need costs nothing; being less specific than you need is how a one-line fix becomes a forty-line diff.

A replacement can also be empty, which simply deletes the matched text:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sed 's/+define+//' filelists/top.f
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
DEBUG
+incdir+/proj/old/rtl
/proj/old/rtl/alu.sv
/proj/old/rtl/fifo.sv
/proj/old/tb/env.sv

Nothing between the last two delimiters means "replace it with nothing". There is no separate delete-text command; this is it.

And two very common shapes worth having in your fingers — adding something to the start of every line, and to the end:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
printf 'clk\nrst\n' | sed 's/^/top./'
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
top.clk
top.rst
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
printf 'alu.sv\n' | sed 's/$/ \\/'
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
alu.sv \

^ means "the start of the line" and $ means "the end of it". They match a position rather than a character, so substituting them inserts text instead of replacing any. The second example appends a line-continuation backslash — exactly what you need when turning a file list into a shell command. Module 4 covers anchors properly.

4. When Nothing Happens

A substitution that finds no match is not an error. sed prints the line unchanged and carries on:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sed 's/NOTHERE/X/' filelists/top.f
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
+define+DEBUG
+incdir+/proj/old/rtl
/proj/old/rtl/alu.sv
/proj/old/rtl/fifo.sv
/proj/old/tb/env.sv

Five lines in, five lines out, nothing changed — and the exit status is 0. sed considers "I looked and it was not there" a complete success, which it is.

5. Only the First Match on Each Line

Here is the default that surprises everyone. Consider a filelist line that mentions the same directory three times:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
cat -n filelists/paths.f
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
     1	+incdir+/proj/old -y /proj/old/rtl -v /proj/old/lib.v
     2	/proj/old/rtl/alu.sv

Line 1 contains /proj/old three times. Migrate the tree:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sed 's|/proj/old|/proj/new|' filelists/paths.f
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
+incdir+/proj/new -y /proj/old/rtl -v /proj/old/lib.v
/proj/new/rtl/alu.sv

Read line 1 carefully. The first /proj/old became /proj/new. The second and third did not. On line 2 there was only one, so it changed and the line looks completely correct.

This is not a bug and not a partial failure — s replaces one match per line, the leftmost one, unless you tell it otherwise. The result is a filelist that is half-migrated in a way that will compile until it suddenly does not.

(Notice this command used | as the delimiter instead of /. The pattern is a path full of slashes, and a slash delimiter would collide with them. Chapter 3.3 is about exactly that choice.)

The fix is a flag — a letter after the closing delimiter — and Chapter 3.2 is entirely about which flag to use: g for every match, or a number for one specific occurrence. For now the thing to carry forward is the default itself: one line, one replacement.

6. Your File Is Not Touched

The third default is the one that makes sed safe to experiment with. Every command so far printed to the terminal. None of them changed anything on disk.

That is easy to claim, so here it is measured. A checksum of the filelist before:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
md5 -q filelists/top.f
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1146cb4a56d442e6dfc78704760b04d1

Now run a substitution that definitely matches, throw its output away, and take the checksum again:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sed 's/DEBUG/RELEASE/' filelists/top.f > /dev/null; md5 -q filelists/top.f
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1146cb4a56d442e6dfc78704760b04d1

Identical. sed read the file, edited its own buffer, printed the result — and left the original byte for byte as it was.

This is why every example in this track has been safe to type. You can run a substitution against real project files all day and the worst outcome is terminal output you do not like.

Editing the file in place takes an extra option, -i, and it is deliberately not demonstrated anywhere in this module. It has real portability traps and real failure modes, it is the subject of Chapter 7.1, and the correct order is to learn to prove a substitution right before you learn to make it permanent.

7. Telling a Real Edit From a No-Op

Section 4 showed the problem: success and failure look the same. Two habits solve it, and both are small enough to use every time.

Print only the lines that changed. Pair -n (which switches off auto-print) with the p flag on the substitution itself. The p here fires only when the substitution actually did something:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sed -n 's|/proj/old|/proj/new|p' filelists/top.f
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
+incdir+/proj/new/rtl
/proj/new/rtl/alu.sv
/proj/new/rtl/fifo.sv
/proj/new/tb/env.sv

Four lines of output from a five-line file. That is the answer to "did it work, and on what": four lines matched, and you are reading exactly what they would become. Line 1 (+define+DEBUG) is absent because it did not match — and if the pattern had been wrong, the output would have been empty, which is unmistakable.

Count them if you prefer a number:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sed -n 's|/proj/old|/proj/new|p' filelists/top.f | wc -l
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
       4

Or look at the change itself. Pipe the full output into diff against the original, using - to mean "the thing coming down the pipe":

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sed 's|/proj/old|/proj/new|g' filelists/top.f | diff -u filelists/top.f -
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
--- filelists/top.f	2026-10-12 16:37:18
+++ -	2026-10-12 16:38:27
@@ -1,5 +1,5 @@
 +define+DEBUG
-+incdir+/proj/old/rtl
-/proj/old/rtl/alu.sv
-/proj/old/rtl/fifo.sv
-/proj/old/tb/env.sv
++incdir+/proj/new/rtl
+/proj/new/rtl/alu.sv
+/proj/new/rtl/fifo.sv
+/proj/new/tb/env.sv

This is the review a colleague would want to see. Every changed line appears twice — once as it was, once as it will be — and +define+DEBUG sits there with a leading space, confirming it was considered and left alone.

diff exits 1 when it finds differences, which reads oddly but is useful: a dry run that exits 0 means your substitution changed nothing. Chapter 7.2 turns both of these into a standard pre-merge procedure.

8. Think It Through

1. What does this print, and why is that a reasonable thing for sed to do?

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sed 's/xyz/abc/' filelists/top.f

The entire five-line file, unchanged, with exit status 0. No line contains xyz, so no substitution fires, and auto-print still prints every line. sed is not reporting a failure because nothing failed — the instruction was "wherever you see xyz, write abc", and it was carried out faithfully zero times. This is why section 7's habits matter: the output of a correct command and the output of a typo are identical.

2. A colleague migrates a filelist with sed 's|/proj/old|/proj/new|' top.f > top.new.f and the build fails with a missing include path. What happened?

Only the first /proj/old on each line was replaced. Any line mentioning the directory more than once — typically the +incdir+ line, which often lists several search paths — is now half-migrated. The compiler finds some paths and not others. The fix is the g flag from Chapter 3.2, and the way to have caught it is the diff from section 7: a half-changed line is obvious in a diff and invisible in a 2,000-line listing.

3. You run a substitution and then cat the file. It is unchanged. Did the command fail?

Almost certainly not — that is the default. sed writes to standard output, so unless you used -i or redirected the output to a file, your file will be unchanged no matter how well the pattern matched. Check whether it worked by looking at sed's output, not at the file.

4. Write a command that turns every line of a filelist into a shell continuation, so alu.sv becomes alu.sv \.

sed 's/$/ \\/' filelists/top.f. $ matches the position at the end of the line, so the replacement is appended rather than replacing anything. The backslash is doubled because a single one would be an escape; section 3 ran exactly this.

5. Why is being over-specific in a pattern cheap, and being under-specific expensive?

Over-specific means you type more and it matches fewer things — if it is too narrow, you get no change, you notice immediately because the output is identical, and you widen it. Under-specific means it matches things you never looked at: the extra matches are somewhere in a long file, the output looks plausible, and nothing tells you. Chapter 7.3 is a whole chapter on that failure.

9. Summary

  • s/pattern/replacement/ — four parts, the delimiter being any character you choose.
  • A pattern is unanchored: it matches anywhere on the line, including inside a longer word.
  • An empty replacement deletes the matched text. ^ and $ let you insert at the start or end of a line.
  • No match is not an error. sed prints the line unchanged and exits 0 — a successful edit and a complete no-op look identical.
  • Only the first match on each line is replaced by default. On the three-match line, two were left behind. Chapter 3.2 covers the flags that change this.
  • Your file is never modified. Proved with a checksum before and after: sed edits a buffer and prints it. -i is Chapter 7.1.
  • Never redirect output onto the input — Chapter 2.4 showed it empties the file and exits 0.
  • Two habits make an edit reviewable: sed -n 's/x/y/p' prints only the lines that changed, and … | diff -u file - shows the change itself. Use one of them before every real edit.

You can now replace one thing in one place. The obvious next question is the one section 5 left open: how do you change all the matches on a line, or only the second one? Chapter 3.2 — Global and Occurrence-Based Replacement answers it, and it is the shortest chapter in the module with the biggest effect on correctness.

Where this fits

Part of the sed curriculum.