kmail.at
← learning

shell · difficulty ◆◆

sed — stream editor for text transforms

Transform every line of a stream in one pass — no editor, no temp file, no fuss.

The command that looks like line noise — s/old/new/g — is the same one that rewrites configs across a thousand servers in a single line.

2026-08-28 · 6 min read

$ sed

What it does

sed reads input line by line, applies the script you give it, and writes the result to stdout. It never edits the file unless you explicitly ask with -i. The core operation is the substitute command s/pattern/replacement/, but sed also deletes lines (d), prints ranges (-n '1,2p'), and filters by address. Because it reads from stdin, it slots into any pipeline: the output of grep, cat, or a log tail can be reshaped before it reaches the next tool.

Why it matters

When you need to change text across many files or a live stream, opening an editor is the wrong tool. sed does it in one non-interactive pass, which makes it scriptable and repeatable. It is the difference between hand-editing a dozen config files and running one command that fixes them all. Master the s/// idiom and the -i flag and you can rename variables, scrub secrets, or normalize logs across an entire tree in seconds.

Example

$ sed 's/x:/XX:/' passwd_sample.txt
root:XX:0:0:root:/root:/bin/bash
daemon:XX:1:1:daemon:/usr/sbin:/usr/sbin/nologin
kmail:XX:1000:1000:kmail:/home/kmail:/bin/bash

Substitute the first occurrence of 'x:' on each line with 'XX:'. The file itself is untouched — the result goes to stdout.

$ sed '/nologin/d' passwd_sample.txt
root:x:0:0:root:/root:/bin/bash
kmail:x:1000:1000:kmail:/home/kmail:/bin/bash

Delete every line matching /nologin/. Addresses like /pattern/ select which lines a command acts on.

$ sed -n '1,2p' passwd_sample.txt
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin

-n suppresses automatic printing; '1,2p' prints only lines 1 through 2. This is how you 'head' a file with sed.

$ sed -i 's/kmail/KMAIL/' passwd_edit.txt
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
KMAIL:x:1000:1000:kmail:/home/kmail:/bin/bash

-i edits the file in place. The first 'kmail' on the last line became 'KMAIL' — the file on disk changed.

$ printf '# comment\n\nserver=8080\n\n# another\nport=3306\n' | sed '/^#/d;/^$/d'
server=8080
port=3306

Strip comment lines and blank lines from a config in one pass — a classic config-cleanup pipeline.

$ echo 'foo foo bar foo' | sed 's/foo/baz/g'
baz baz bar baz

The g flag makes the substitution global — every 'foo' on the line, not just the first.

Common flags

s/old/new/
substitute the first match of 'old' with 'new' on each line
s/old/new/g
global — replace every match on the line, not just the first
-n
suppress automatic line printing; print only what you explicitly ask with p
-i
edit the file in place (use -i.bak to keep a backup)
-e
add a script expression; lets you chain multiple commands
-E
extended regex — so +, ?, ( ) work without backslashes
d
delete matching lines (e.g. /pattern/d or '1,5d')

History

Origin

sed was written by Lee E. McMahon in 1974 at Bell Labs, as a command-line version of the editing commands in the ed text editor. The name is short for 'stream editor'. Where ed was interactive, sed was designed to run non-interactively over a stream — which is exactly what made it scriptable and why it survived while ed faded.

The s/// legacy

The substitute syntax s/pattern/replacement/ that sed popularized became so universal that Perl, awk, and even modern editors all borrowed it. When you type :%s/foo/bar/g in vim, you are using sed's grammar. GNU sed 4.9, the version on most Linux boxes, still runs the same core language McMahon designed in 1974.

Fun facts

Pros & cons

pros

  • + Non-interactive and scriptable — the same transform runs on one file or a thousand
  • + Streams from stdin, so it composes with every other Unix filter
  • + In-place editing (-i) makes bulk config changes a one-liner

cons

  • − The terse syntax is hard to read at a glance — s/old/new/g looks like line noise until you know it
  • − Line-oriented: it struggles with edits that span multiple lines
  • − Regex escaping rules differ between basic and extended mode, a classic source of confusion

Takeaways

  1. 1s/old/new/ replaces the first match per line; add g to replace every match
  2. 2Use -n with p to print only selected lines — sed's version of head
  3. 3Back up with -i.bak before editing files in place
  4. 4Chain commands with -e or semicolons: sed -e 's/a/b/' -e '/^#/d'
  5. 5Reach for -E for extended regex so +, ?, and ( ) work without backslashes

Related commands