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.
The route
Jump straight to the step you need, or tick off Done means at the end.
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
- Nothing appears:
-nsuppresses automatic output. Add a command such asp, or remove-nwhen you want every transformed line. - Every line disappears: check whether an address was omitted before
d. Run the script without-iand compare its output with the input. - Only the first occurrence changed: add the
gflag to the substitution. - Several files behave as one stream: line numbers normally continue across input files. Use
-swhen each file must be treated separately. - A path or replacement contains slashes: choose a different delimiter, such as
s|old/path|new/path|, and still quote the script.
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
- Version checked. You know the installed GNU sed version and whether an option is GNU-specific.
- Printing is deliberate. You used
-non purpose when printing selected or substituted lines. - Scripts are quoted and scoped. You chose
g,-Eand alternate delimiters for a stated reason. - Dry run done. You previewed a file transformation and reviewed the result before using
-i. - Backup confirmed. Any in-place edit has a recoverable backup, and you know how to restore it.