Guides

sed command: find and replace, delete lines, and edit files in place

sed 's/old/new/g' replaces every match; -i edits the file in place. Addressing by line number or pattern, deleting lines, and the GNU vs BSD -i difference that corrupts files on macOS.

Published ~7 min read

Short answer: sed 's/old/new/' file prints the file with the first match on each line replaced, without touching the file; add g for every match, not just the first, and -i to edit the file in place. Every command below was tested with GNU sed 4.9 (Debian 12 and Ubuntu); macOS ships an older BSD sed with a different -i syntax, noted where it differs.

TaskCommand
Replace first match per linesed 's/old/new/' file
Replace every match on every linesed 's/old/new/g' file
Edit the file in placesed -i 's/old/new/g' file
Edit in place, keep a .bak backupsed -i.bak 's/old/new/g' file
Case-insensitive matchsed 's/old/new/gi' file
Only on lines matching a patternsed '/pattern/s/old/new/' file
Print only lines 10-20sed -n '10,20p' file
Delete lines matching a patternsed '/pattern/d' file
Delete a specific line numbersed '3d' file
Several edits in one passsed -e 's/a/b/' -e 's/c/d/' file
Use a different delimiter (for paths)sed 's#/old/path#/new/path#' file
Insert a line before a matchsed '/pattern/i\\new line' file
Append a line after a matchsed '/pattern/a\\new line' file

The substitute command: s/old/new/

Linux · macOS
printf "foo bar\nbaz foo\n" > test.txt
sed 's/foo/XXX/' test.txt
Output
XXX bar
baz foo

Only the first match on each line changes by default; the second foo on the second line is a different line, so it changes too, but a second foo on the same line would not, without the g flag:

Linux · macOS
echo "foo foo foo" | sed 's/foo/XXX/'     # XXX foo foo — only the first
echo "foo foo foo" | sed 's/foo/XXX/g'    # XXX XXX XXX — every match
echo "foo foo foo" | sed 's/foo/XXX/2'    # foo XXX foo — only the second match
echo "foo foo foo" | sed 's/foo/XXX/2g'   # foo XXX XXX — from the second match onward

Using another delimiter for paths

The slash after s is a delimiter, not fixed punctuation — sed accepts almost any character there, which matters the moment the text itself contains slashes, as a file path does:

awkward with /
sed 's/\/var\/www\/old/\/var\/www\/new/' nginx.conf
clean with # instead
sed 's#/var/www/old#/var/www/new#' nginx.conf

Case-insensitive replacement

Linux · macOS
echo "Hello World" | sed 's/hello/hi/i'

GNU sed’s I flag (also lowercase i) is case-insensitive only for that one substitution, not the whole command line.

Editing the file itself: -i

GNU sed (Debian, Ubuntu, most Linux)
sed -i 's/old/new/g' config.conf          # edits in place, no backup
sed -i.bak 's/old/new/g' config.conf      # edits in place, keeps config.conf.bak

Without -i, sed only ever prints to standard output and never touches the file — the safe way to try an expression before committing to it. GNU sed’s -i takes the backup suffix glued directly to the flag with no space (-i.bak, not -i .bak); with no suffix at all it edits silently with no backup, which is fine for a throwaway file and risky for a server config you did not back up some other way first.

Addressing: which lines a command touches

Every sed command can be limited to specific lines by putting an address in front of it — a line number, a range, or a pattern that has to match first.

AddressMeans
3Line 3 only
10,20Lines 10 through 20
5,$Line 5 to the end of the file
/pattern/Every line matching the pattern
/start/,/end/From the first line matching start to the next matching end
1~2Every other line, starting at 1 (GNU sed only)
only replace inside a specific block
sed '/^\[database\]/,/^\[/{s/host=old/host=new/}' config.ini

That line only touches the [database] section of an INI-style config, leaving an identically named key elsewhere in the file alone.

print only lines 10-20 (like sed's answer to head/tail -n)
sed -n '10,20p' file.txt

-n turns off sed’s default behaviour of printing every line; without it, 10,20p would print those ten lines twice. This combination is a common, if old-fashioned, alternative to head and tail piped together.

delete lines instead of printing them
sed '2d' file.txt                 # delete line 2
sed '/^#/d' file.txt              # delete every comment line
sed '/^$/d' file.txt              # delete every blank line
sed '$d' file.txt                 # delete the last line

Several edits in one pass: -e and semicolons

Linux · macOS
sed -e 's/foo/bar/' -e 's/baz/qux/' file.txt
sed 's/foo/bar/; s/baz/qux/' file.txt

Both run the two substitutions on every line in one read of the file, which matters for a large file — piping sed into another sed reads the data twice for no reason.

Inserting and appending lines

GNU sed
sed '/^\[Service\]/a Restart=always' myapp.service
sed '/^server {/i # managed by deploy script' nginx.conf

a (append) adds the new line after the matched one; i (insert) adds it before. GNU sed accepts the text directly after a/i with just a space, as above; the older, more portable form needs a backslash and a literal newline (sed '/pattern/a\ on one line, the new text on the next) — worth knowing if a script has to run on both GNU and BSD sed.

Everyday sed one-liners

TaskCommand
Remove Windows line endings (CRLF to LF)sed -i 's/\\r$//' file.sh
Remove trailing whitespacesed -i 's/[ \\t]*$//' file.txt
Comment out a config linesed -i 's/^PermitRootLogin yes/#PermitRootLogin yes/' sshd_config
Uncomment a config linesed -i 's/^#PasswordAuthentication/PasswordAuthentication/' sshd_config
Number every linesed = file.txt | sed 'N;s/\\n/\\t/'
Double-space a filesed G file.txt
Print everything between two markerssed -n '/START/,/END/p' file.txt

The CRLF fix is the one that shows up most on a server: a script edited on Windows and uploaded fails with /bin/bash^M: bad interpreter, which that troubleshooting guide covers in full, including why file script.sh is the way to confirm it before reaching for sed at all.

Basic vs extended regular expressions

By default sed uses POSIX Basic Regular Expressions, where (, ), {"{"}, {"}"}, + and | are literal characters unless escaped with a backslash — the opposite of what most other regex tools do. -E (or -r on GNU sed, an older alias) switches to Extended Regular Expressions, where those characters work unescaped:

basic (default)
echo "v1.2.3" | sed 's/[0-9]\+/X/g'       # need \+ to mean "one or more"
extended
echo "v1.2.3" | sed -E 's/[0-9]+/X/g'      # + works directly
echo "cat or dog" | sed -E 's/(cat|dog)/pet/'

sed vs grep vs awk

These three get reached for in similar moments and do different jobs: grep finds and prints lines, sed transforms lines (substitute, insert, delete) and passes the rest through unchanged, awk processes structured, column-like data and can compute things grep and sed cannot. Finding a line is grep; changing text inside a file, in place, from a script, is sed’s job specifically — the one the examples above cover throughout this site’s own deploy snippets, such as fixing a config value inside the SSH config file without opening an editor.

Frequently asked

How do I replace text in a file with sed?

sed 's/old/new/' file replaces only the first match on each line and prints the result without touching the file; add the g flag, sed 's/old/new/g' file, to replace every match on every line. Add -i to edit the file itself: sed -i 's/old/new/g' file.

How do I edit a file in place and keep a backup with sed?

sed -i.bak 's/old/new/g' file edits the file and saves the original as file.bak. GNU sed (Linux) takes the suffix glued directly to -i with no space; BSD sed on macOS requires the suffix as its own separate argument, even an empty one (sed -i '' '...' file) for no backup, which is the single most common sed mistake when a script moves from Linux to a Mac.

How do I delete specific lines with sed?

sed '3d' file deletes line 3; sed '/pattern/d' file deletes every line matching a pattern; sed '/^$/d' file deletes every blank line. None of these change the file unless you add -i.

What is the difference between sed and grep?

grep finds and prints matching lines; it cannot change a file. sed transforms lines — substitute, insert, delete — and passes every other line through unchanged, which is the tool for editing a file's contents from a script rather than only searching it.

Related articles

All guides