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.
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.
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.
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.
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.
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.
-n suppresses automatic output. Add a command such as p, or remove -n when you want every transformed line.d. Run the script without -i and compare its output with the input.g flag to the substitution.-s when each file must be treated separately.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.
-n on purpose when printing selected or substituted lines.g, -E and alternate delimiters for a stated reason.-i.