Find and Replace in a Stream: sed

sed changes text as it flows past, replacing words with s/old/new/g and deleting lines with d, and sed -i.bak changes the files themselves while keeping a backup of each one.

Journeyman · Unix · Linux · Updated

After this lesson you can

  • Replace text with sed 's/old/new/g', and delete lines by number or by pattern
  • Change files in place with sed -i.bak, after checking the output first and keeping a backup

Key takeaways

  • sed 's/old/new/' replaces the first match on each line, and a g at the end replaces every match. sed prints the result; the file stays as it was.
  • When the text has slashes in it, choose another separator, such as s|/|-|g.
  • sed '/^#/d' deletes the lines that match a pattern, and sed '1,2d' deletes lines by their numbers.
  • sed -i.bak writes the change into the files and keeps each old version as a .bak file. Check the output without -i first.
  • A Mac's sed needs something right after -i. -i.bak works on both; for no backup, a Mac takes -i '' and Linux takes -i alone.

The bakery's website has a page for every day's specials, and the new website wants dates written 2026-11-09 instead of 2026/11/09. There are 50 pages. Opening each one in an editor, finding the date and retyping it takes an afternoon, and one missed page keeps the old format. sed, the stream editor, makes the same change on every line of every file in one command. Web developers use it to change a setting across a whole project, and IT administrators use it to fix a value in a settings file on a server that has no mouse.

Set up the practice folder

These lines make the bakery's website folder: a menu and three days of specials. printf '%s\n' with several quoted pieces writes each piece on a line of its own. The lines are safe to run again: each printf replaces its file.

mkdir -p ~/terminal-practice/bakery-site/specials && cd ~/terminal-practice/bakery-site
printf '%s\n' '# Bluebird Bakery menu' '# Prices in dollars' 'sourdough loaf: 7.00' 'choc chip cookie: 2.00' 'cinnamon roll: 3.50' 'blueberry muffin: 3.00' > menu.txt
printf 'Posted 2026/11/09\nPumpkin bread: 4.50\n' > specials/monday.txt
printf 'Posted 2026/11/10\nApple hand pie: 3.75\n' > specials/tuesday.txt
printf 'Posted 2026/11/11\nCranberry scone: 3.25\n' > specials/wednesday.txt

s/old/new/: replace as the text flows past

Picture a proofreader beside a conveyor belt with a red pen. Every line rolls past, the pen fixes the words it was told to fix, and the lines roll on out the other end. The original page stays in the drawer. That's sed: it reads a file line by line, changes each line, and prints the result.

The s command (for substitute) takes the old text and the new, each followed by a slash: s/old/new/, read "substitute old with new". The bakery renamed its cookie:

sed 's/choc chip/chocolate chip/' menu.txt
# Bluebird Bakery menu
# Prices in dollars
sourdough loaf: 7.00
chocolate chip cookie: 2.00
cinnamon roll: 3.50
blueberry muffin: 3.00

The single quotes keep the shell from touching the command, spaces and all. And sed only printed the change. The file is as it was:

grep chip menu.txt
choc chip cookie: 2.00

That's what makes sed safe to try: run a change as often as you like and read the result before anything is saved. The old text is a pattern, the same kind grep uses (Patterns: Regular Expressions for grep), so ^, $ and . mean what they mean there, and capital letters must match.

g, and a separator of your choice

Now the dates. The text to replace is a slash, and slashes also separate the parts of s. Writing s/\//-/ works (\/ is a slash that doesn't count as a separator), but it's hard to read. So sed lets you choose the separator: whatever comes right after s. With |, read "pipe" here too, it becomes s|/|-|:

sed 's|/|-|' specials/monday.txt
Posted 2026-11/09
Pumpkin bread: 4.50

Only the first slash changed. On its own, s replaces the first match on each line. A g at the end (for global) replaces every match:

sed 's|/|-|g' specials/monday.txt
Posted 2026-11-09
Pumpkin bread: 4.50

d: delete lines

The d command deletes lines instead of changing them. In front of it goes which lines: a pattern between slashes, or line numbers. The menu's two comment lines start with #, and ^# (read "starts with hash") matches them:

sed '/^#/d' menu.txt
sourdough loaf: 7.00
choc chip cookie: 2.00
cinnamon roll: 3.50
blueberry muffin: 3.00

By number, sed '1d' deletes line 1 and sed '1,2d' deletes lines 1 to 2, which here prints the same four lines. sed '1d' is a quick way to drop the header line of a CSV file. Again, the file itself is untouched.

-i.bak: change the files themselves

When the output looks right, -i (for in place) writes the change back into each file instead of printing it. This is the step that changes your files, so make it a habit: run the command without -i first and read what it prints, then run it again with -i.bak. The .bak tells sed to keep each file's old version beside it, named with .bak on the end. The * pattern hands sed every specials page at once, whether there are 3 or 50:

sed -i.bak 's|/|-|g' specials/*.txt
ls specials
cat specials/monday.txt
monday.txt      tuesday.txt      wednesday.txt
monday.txt.bak  tuesday.txt.bak  wednesday.txt.bak
Posted 2026-11-09
Pumpkin bread: 4.50

sed printed nothing itself: with -i, its output went into the files. If a change goes wrong, the backup puts it right: mv specials/monday.txt.bak specials/monday.txt. Run it once, though: a second run backs up the already-changed files, over the first backups. Once you're happy, delete the backups with rm specials/*.bak, after looking with ls specials/*.bak first, as in Deleting Safely: rm and rmdir.

On a Mac, sed is a different version, and -i is where it differs most. -i.bak, with the ending written right after -i, works on both. Without a backup, Linux takes sed -i 's|/|-|g' file and a Mac needs an empty ending, sed -i '' 's|/|-|g' file. Each of those two fails on the other system, which is one more reason to keep the backup.

Check yourself

2 questions to try on the lesson page.

So what

s/old/new/g on a stream, and -i.bak once the output looks right, turns an afternoon of opening files into one command you can check before it changes anything.

Key terms

Cheat sheets

Related