# 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 November 10, 2026

## 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.

```bash
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:

```bash
sed 's/choc chip/chocolate chip/' menu.txt
```

```text
# 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:

```bash
grep chip menu.txt
```

```text
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|/|-|`:

```bash
sed 's|/|-|' specials/monday.txt
```

```text
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:

```bash
sed 's|/|-|g' specials/monday.txt
```

```text
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:

```bash
sed '/^#/d' menu.txt
```

```text
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:

```bash
sed -i.bak 's|/|-|g' specials/*.txt
ls specials
cat specials/monday.txt
```

```text
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

- [sed](https://www.ask-codey.com/glossary/sed)
- [In-Place Editing](https://www.ask-codey.com/glossary/in-place-editing)
- [Regular Expression in the Shell](https://www.ask-codey.com/glossary/regular-expression-in-the-shell)

## Cheat sheets

- [Find and Replace in a Stream: sed](https://www.ask-codey.com/cheat-sheets/bash-sed-cheat-sheet)

## Related

- [Patterns: Regular Expressions for grep](https://www.ask-codey.com/lessons/bash-regular-expressions)
- [Columns and Totals: awk](https://www.ask-codey.com/lessons/bash-awk)

Source: https://www.ask-codey.com/lessons/bash-sed
