sed Part II · Module 5 · Filtering and Line Operations
Inserting, Appending, and Changing Text
Every command so far has worked on text that was already in the file. These three create it: i puts a line before the matched one, a puts a line after it, and c replaces it. That makes them the right tool for a generated-file header, an extra flag after a matched option, or swapping out a whole configuration line. They are also where GNU and BSD sed disagree most sharply — the convenient one-line form you will find in most examples online is a GNU extension that fails outright on a Mac. And all three behave unlike anything else in sed in a way worth knowing: the text they produce never enters the pattern space, so the rest of your script cannot see it.
Intermediate19 min readsedPart IIPortabilityFilelistsTool Scripts
Part II · Module 5 · Chapter 5.3 · Inserting, Appending, and Changing Text
1. Three Commands That Make Lines
i insert — put text BEFORE the addressed line
a append — put text AFTER the addressed line
c change — REPLACE the addressed line with textThey work on whole lines. There is no "insert in the middle of a line" — that is what s is for. If you want to add text to an existing line, Chapter 3.3's & is the tool; if you want a new line in the output, these are.
Typical jobs:
i— a// AUTOGENERATEDbanner at the top of a file your flow writes.a— an extra compile option on the line after a matched tool invocation.c— replacing a wholeset link_libraryline with the new release's value.
2. The Form You Will See Online
Most examples on the internet are written like this:
sed '1i // AUTOGENERATED' put the text right after the command letterThat is the GNU one-line form, and it is genuinely convenient. It is also a GNU extension.
3. Why Those Examples Fail on a Mac
All three of this chapter's registered examples use that form. Here they are, run as written:
sed '1i // AUTOGENERATED - do not edit' filelists/top.fsed: 1: "1i // AUTOGENERATED - d ...": command i expects \ followed by textsed '/^vcs /a -full64' sim/run.shsed: 1: "/^vcs /a -full64
": command a expects \ followed by textsed '/^set link_library/c set link_library { * std.db }' synth/dc.tclsed: 1: "/^set link_library/c se ...": command c expects \ followed by textAll three exit with status 1. The message is unusually helpful: this command expects a backslash followed by the text.
This is the good kind of portability failure — loud, immediate, and it tells you the fix. Compare it with the silent ones this track has measured: GNU's \U emitting a literal U (Chapter 3.3), \| matching nothing (Chapter 4.1), and 0,/re/ returning nothing (Chapter 4.4). Those three run happily and give you wrong answers. These stop.
4. The Form That Works Everywhere
POSIX spells it with a backslash, a real newline, and then the text:
sed '1i\
TEXT'The newline inside the quotes is mandatory — that is what the error message was asking for. In a shell script this means the command spans two lines, which looks odd the first time and is completely normal.
Inserting a header:
sed '1i\
// AUTOGENERATED - do not edit' filelists/top.f// AUTOGENERATED - do not edit
+define+DEBUG
# indented comment
+incdir+/proj/old/rtl
./rtl/alu.sv
# a commented-out file
/proj/old/tb/env.sv
./rtl/top.svThe header is on top and the original nine lines follow, unchanged.
Appending an option after a matched tool line:
cat -n sim/run.sh 1 #!/bin/sh
2 vcs -sverilog -timescale=1ns/1ps top.sv
3 ./simv +UVM_TESTNAME=smokesed '/^vcs /a\
-full64' sim/run.sh#!/bin/sh
vcs -sverilog -timescale=1ns/1ps top.sv
-full64
./simv +UVM_TESTNAME=smokeThe new line landed after the vcs line. Note the two leading spaces survived — on the sed used here, leading whitespace in the text is preserved, which cat -et confirms:
printf 'x\n' | sed 'a\
indented' | cat -etx$
indented$(Some older seds strip leading whitespace from a and i text unless you escape the first space with a backslash. If your indentation disappears on another machine, \ indented is the defensive spelling.)
Changing a whole line:
cat -n synth/dc.tcl 1 set search_path { /libs/v1 }
2 set link_library { * sc_tt.db }
3 set target_library { sc_tt.db }sed '/^set link_library/c\
set link_library { * std.db }' synth/dc.tclset search_path { /libs/v1 }
set link_library { * std.db }
set target_library { sc_tt.db }Line 2 replaced entirely. c is the right choice over s when the whole line is being rewritten — you state the new line once, instead of writing a pattern that has to match the old one exactly.
Several lines at once. End each line of text with a backslash except the last:
sed '1i\
// AUTOGENERATED\
// do not edit' filelists/top.f | head -4// AUTOGENERATED
// do not edit
+define+DEBUG
# indented comment5. c on a Range Collapses the Whole Range
i and a behave the same whether their address is one line or many. c does not, and the difference is large.
With a plain pattern address, c replaces each matching line individually:
cat -n sta/timing.rpt 1 Startpoint: u_alu/rd_data_reg[3]
2 Endpoint: u_fifo/wr_ptr_reg[0]
3 data arrival time 1.842
4 data required time 2.100
5 slack (MET) 0.258
6 Startpoint: u_fifo/rd_ptr_reg[1]
7 Endpoint: u_alu/op_reg[2]
8 data arrival time 2.410
9 slack (VIOLATED) -0.110sed '/^Endpoint/c\
[ENDPOINT]' sta/timing.rptStartpoint: u_alu/rd_data_reg[3]
[ENDPOINT]
data arrival time 1.842
data required time 2.100
slack (MET) 0.258
Startpoint: u_fifo/rd_ptr_reg[1]
[ENDPOINT]
data arrival time 2.410
slack (VIOLATED) -0.110Nine lines in, nine lines out — each Endpoint line swapped for the replacement.
With a range, c replaces the entire range with a single copy of the text:
sed '/^Startpoint/,/slack/c\
[PATH REMOVED]' sta/timing.rpt[PATH REMOVED]
[PATH REMOVED]Nine lines in, two out. The first path block, lines 1 to 5, became one line. Then — exactly as Chapter 4.4 described — the range reopened on line 6 and the second block, lines 6 to 9, became the other.
That is a genuinely useful operation when you mean it: "replace each timing path with a one-line marker" is a real log-reduction task. It is also a severe surprise if you expected c to work line by line. The rule:
/pat/c\TEXT one replacement per matching line
/a/,/b/c\TEXT one replacement per RANGE OCCURRENCE6. The Text Never Enters the Pattern Space
This is the behaviour that makes i, a and c different from everything else in sed, and nothing else in the module predicts it.
Text from these commands is queued for output directly. It is not put into the pattern space, so no later command in your script can match it, change it, or delete it.
Append a flag, then try to rewrite it:
printf 'vcs top.sv\n' | sed -e 'a\
-full64' -e 's/-full64/XXX/'vcs top.sv
-full64The substitution did nothing. It ran against the pattern space, which still held vcs top.sv — the appended text was never there to be matched. On GNU or BSD alike, appended text is output, not buffer.
The same property in the other direction: i writes its text immediately, and a later d cannot take it back:
printf 'x\ny\n' | sed -e '/x/i\
BEFORE-X' -e '/x/d'BEFORE-X
yBEFORE-X was inserted, then the x line itself was deleted — and the inserted line stayed. The insert had already happened; d ends the cycle for the pattern space, not for output that has already been emitted.
7. One Syntax Trap
With the backslash form, the text runs to the end of the line — including anything that looks like another command:
printf 'x\n' | sed -e 'a\
TEXT; p'x
TEXT; pThe ; p became part of the appended text rather than a second command. That is correct behaviour and it catches people who have got used to chaining commands with semicolons.
The fix is to give the next command its own -e, or its own line. Chapter 5.4 is about composing multi-command scripts and returns to this.
8. A Related Command, Named Only
r filename reads an entire file in and queues it for output after the addressed line — a for a whole file rather than a line of text. It shares the queueing behaviour from section 6 exactly, so the file's contents are likewise invisible to the rest of the script. It is not part of this chapter's scope; it appears where a flow needs it, in Module 8's composition work.
9. Think It Through
1. A colleague's script has sed '1i // header' file and it fails on your Mac but works on their Linux box. What exactly do you change?
Replace the one-line form with the POSIX backslash-newline form:
sed '1i\
// header' fileThe error message — command i expects \ followed by text — is telling you precisely this. Note the newline must be real; 1i\\// header on one line is not the same thing.
2. When should you use c rather than s?
When the whole line is being replaced. c states the new line once; s requires a pattern that matches the old line, which you then have to keep in step with reality. For set link_library { * sc_tt.db } becoming set link_library { * std.db }, c addressed by /^set link_library/ keeps working when the old value changes, while an s that spells out the old value stops matching the day someone edits it.
3. You run sed '/^Startpoint/,/slack/c\…' on a report with four timing paths. How many replacement lines do you get?
Four — one per range occurrence, not one for the file and not one per input line. The range reopens at each Startpoint, and each occurrence collapses to a single copy of the text. Section 5 measured two from a two-path report.
4. Why can a later substitution not rewrite text you appended?
Because appended text never reaches the pattern space — section 6 measured it. The substitution runs against the input line, which does not contain the appended text at all. To change it, change the text in the a command; to post-process it, pipe into a second sed, where the appended line arrives as ordinary input.
While testing this, one more portability note surfaced. The -e 'a\' -e 'TEXT' split form, often recommended as a way to keep the command on one shell line, does not work on the sed used here:
printf 'vcs top.sv\n' | sed -e 'a\' -e 'NEW'sed: 1: "NEW
": extra characters at the end of N commandExit 1 — the second -e was parsed as an N command rather than as the append text. Use a real newline inside a single -e, as section 4 does.
5. You want a header inserted only if the file does not already have one. Can sed do that?
Not in one pass, because deciding requires knowing something about a line you have not read yet — and Chapter 2.1 established that sed holds one line at a time with no memory between them. The practical answers are outside sed: test first with grep -q, and insert only if it is absent, or make the operation idempotent by deleting any existing header before inserting the new one. Module 6's hold space can carry state, but a grep -q guard is the honest answer for this.
6. Does a put the text after the current line, or at the end of the file?
After the current line — the one its address selected. $a\ is how you get "at the end of the file", because $ is the address for the last line. Without an address, a appends after every line, which is occasionally what you want and more often a surprise.
10. Summary
iinserts before,aappends after,creplaces — all three operate on whole lines, never part of one.- The one-line form
sed '1i text'is a GNU extension. On BSD sed all three commands fail withcommand X expects \ followed by text, exit 1 — a loud, self-documenting failure, unlike the silent GNU-isms in Chapters 3.3, 4.1 and 4.4. - The portable form is a backslash then a real newline then the text. Continue lines with a trailing backslash for multi-line text.
- Leading whitespace in the text is preserved on the sed used here; escape the first space if you need it defensively elsewhere.
con a plain address replaces each matching line;con a range replaces the whole range with one line — a nine-line two-path report became two lines, because the range reopened once.- The text never enters the pattern space.
a\ -full64followed bys/-full64/XXX/left the appended line untouched, and anisurvived a laterdon the same line. - With the backslash form, the text runs to end of line —
TEXT; pappended the semicolon and thepas literal text. cbeatsswhen the whole line changes, because you state the new line instead of describing the old one.
You now have the full line-level command set: print, delete, insert, append, change, substitute. What is left is putting several of them in one script and controlling where each one applies — which matters more than it sounds, because a substitution that is correct inside one region of a report is usually wrong outside it. Chapter 5.4 — Combining Commands and Scoped Operations closes Module 5 with { } blocks and the address scoping that makes a multi-command script safe.
Where this fits
Part of the sed curriculum.
