Use sed Safely for Replacements and In-Place Edits

Half the sed disasters come from one flag typed in the wrong place, so this walks through selecting, replacing and editing a file in place safely. The examples match GNU sed 4.9, installed from the Debian sed package on this machine. Allow about 15 minutes, including a quick review of the output before changing a file.

You need a shell and readable input. The normal examples read from standard input or a file and write to standard output, so they need no elevated privileges. Use sudo only when the file permissions genuinely require it, not as a default way to run a text filter.

1. Check the installed command

Confirm which executable will run and record its version before relying on GNU-specific options:

$ command -v sed
/usr/bin/sed
$ sed --version | head -1
sed (GNU sed) 4.9

GNU sed processes an input stream one line at a time. With no input file it reads standard input; with file names it reads those files. If you omit -e and -f, the first non-option argument is the script, so this is valid:

$ printf '%s\n' apple pear | sed 's/pear/plum/'
apple
plum

Checkpoint: If sed reports an unexpected script or file error, check the order of the arguments. Put the script before input file names, or use -e to make the boundary explicit.

2. Select lines without changing the input

An address limits a command to matching lines. A number selects one line, a regular expression selects matching lines, and a pair such as 2,4 selects an inclusive range:

$ printf '%s\n' one two three four | sed -n '2,3p'
two
three
$ printf '%s\n' alpha beta alpha | sed -n '/^alpha$/p'
alpha
alpha

The -n option suppresses sed's automatic printing. That matters when the script uses p: without -n, a selected line prints once automatically and once by p.

$ printf '%s\n' alpha beta | sed -n 's/^alpha$/ALPHA/p'
ALPHA

This substitution both changes the pattern space and prints only when the substitution succeeds, which makes it a useful filter: an unmatched line produces no output at all.

3. Replace text with a substitution

The common command is s/regular-expression/replacement/flags. The delimiter is normally a slash, but another character can cut down on escaping when the text contains paths. Without a flag, only the first match on each selected line is replaced:

$ printf '%s\n' 'red red' 'red blue' | sed 's/red/green/'
green red
green blue
$ printf '%s\n' 'red red' | sed 's|red|green|g'
green green

The g flag replaces every match on each line. In a replacement, & means the text that matched, and \1 through \9 refer to captured groups. Use -E when you want extended regular expressions, such as unescaped parentheses for a group:

$ printf '%s\n' 'user=alice' 'host=server' | sed -E 's/^([^=]+)=([^=]+)$/\1: \2/'
user: alice
host: server

In shell scripts, quote the complete sed script. That stops the shell expanding characters that belong to sed's regular expression or replacement.

4. Build a readable transformation

Several expressions can be supplied with -e, and a script file can be supplied with -f, which keeps a longer transformation reviewable:

$ printf '%s\n' 'alice' 'bob' 'alice' | sed -e 's/^/user: /' -e '/bob/d'
user: alice
user: alice

The commands run in the order supplied: here the prefix is added first, then the line containing bob is deleted. The d command discards the current line and starts the next cycle. Keep destructive-looking commands such as d behind an explicit address so a typo cannot wipe every line from the output.

For a reusable script, create a file such as normalise.sed containing:

s/[[:space:]]\+$//
s/^INFO: /info: /

Then test it by writing to a new output:

$ sed -f normalise.sed input.txt > output.txt
$ diff -u input.txt output.txt

A non-zero diff status means the files differ, not that sed failed. Review the diff before replacing the original.

5. Edit a file only after a dry run

By default sed writes transformed text to standard output and leaves the input file alone. Treat -i as a state-changing operation and preview the exact transformation first:

$ sed 's/^enabled=false$/enabled=true/' app.conf

When the output is correct, use an explicit backup suffix. GNU sed writes the backup first, then replaces the original path:

$ sed -i.bak 's/^enabled=false$/enabled=true/' app.conf
$ grep -n '^enabled=' app.conf
12:enabled=true
$ diff -u app.conf.bak app.conf
--- app.conf.bak
+++ app.conf
@@
-enabled=false
+enabled=true

There is no undo command inside sed. Recovery is straightforward while the backup exists:

$ cp --preserve=all app.conf.bak app.conf

Warning: Do not use -i on a valuable file until you have a tested backup or version control. With -i and no suffix, GNU sed keeps no backup at all. A failed regular expression can still leave a file changed, and shell redirection with > can truncate an existing destination before sed even starts.

6. Handle common traps

Sed is line-oriented. It is a poor fit for data whose meaning depends on nested structure, quoted delimiters or arbitrary multiline records. Reach for a real parser on structured formats instead of piling on clever substitutions.

Done means