Substitution
Substitution Using Shell Variables
Variables inside single quotes '
don’t get expanded by POSIX compatible shells, so using a shell variable in a sed
substitution requires the use of double quotes "
instead of single quotes '
:
$ var="he"
$ echo "hello" | sed "s/$var/XX/"
XXllo
$ var="he"
$ echo "hello" | sed 's/$var/XX/'
hello
Be careful of command injection when evaluating variables:
$ var='./&/;x;w/etc/passwd
> x;s/he'
$ echo "hello" | sed "s/$var/XX/"
sed: /etc/passwd: Permission denied
If the above was run as root the output would have been indistinguishable from the first example, and the contents of /etc/passwd
would be destroyed.
Backreference
Using escaped brackets, you can define a capturing group in a pattern that can be backreferenced in the substitution string with \1
:
$ echo Hello world! | sed 's/\(Hello\) world!/\1 sed/'
Hello sed
With multiple groups:
$ echo one two three | sed 's/\(one\) \(two\) \(three\)/\3 \2 \1/'
three two one
When using extended regular expressions (see Additional Options) parenthesis perform grouping by default, and do not have to be escaped:
$ echo one two three | sed -E 's/(one) (two) (three)/\3 \2 \1/'
three two one
Words consisting of letter, digits and underscores can be matched using the expression
[[:alnum:]_]\{1,\}
:
$ echo Hello 123 reg_exp | sed 's/\([[:alnum:]_]\{1,\}\) \([[:alnum:]_]\{1,\}\) \([[:alnum:]_]\{1,\}\)/\3 \2 \1/'
reg_exp 123 Hello
The sequence \w
is equivalent to [[:alnum:]_]
$ echo Hello 123 reg_exp | sed 's/\(\w\w*\) \(\w\w*\) \(\w\w*\)/\3 \2 \1/'
reg_exp 123 Hello
Using different delimiters
POSIX/IEEE Open Group Base Specification says:
[2addr] s/BRE/replacement/flags
Substitute the replacement string for instances of the BRE in the pattern space. Any character other than backslash or newline can be used instead of a slash to delimit the BRE and the replacement. Within the BRE and the replacement, the BRE delimiter itself can be used as a literal character if it is preceded by a backslash.
There are cases when the delimiter /
for sed
replacement is in the BRE or replacement, triggering errors like:
$ echo "2/3/4" | sed "s/2/3/X/"
sed: -e expression #1, char 7: unknown option to `s'
For this, we can use different delimiters such as #
or _
or even a space:
$ echo "2/3/4" | sed "s#2/3#X#"
X/4
$ echo "2/3/4" | sed "s_2/3_X_"
X/4
$ echo "2/3/4" | sed "s 2/3 X "
X/4
Pattern flags - occurrence replacement
If we want to replace only the first occurrence in a line, we use sed
as usual:
$ cat example
aaaaabbbbb
aaaaaccccc
aaaaaddddd
$ sed 's/a/x/' example
xaaaabbbbb
xaaaaccccc
xaaaaddddd
But what if we want to replace all occurrences?
We just add the g
pattern flag at the end:
$ sed 's/a/x/g' example
xxxxxbbbbb
xxxxxccccc
xxxxxddddd
And if we want to replace one specific occurrence, we can actually specify which one:
$ sed 's/a/x/3' example
aaxaabbbbb
aaxaaccccc
aaxaaddddd
/3
being the 3rd occurrence.
From info sed
, see GNU sed manual for online version
the POSIX standard does not specify what should happen when you mix the
g
and NUMBER modifiers, and currently there is no widely agreed upon meaning acrosssed
implementations. For GNUsed
, the interaction is defined to be: ignore matches before the NUMBERth, and then match and replace all matches from the NUMBERth on.
$ sed 's/b/y/2g' example
aaaaabyyyy
aaaaaccccc
aaaaaddddd
$ sed 's/c/z/g3' example
aaaaabbbbb
aaaaacczzz
aaaaaddddd