19

When we match a pattern using sed, the matched pattern is stored in the "ampersand" (&) variable. IS there a way to replace a character in this matched pattern using the ampersand itself ?

For example, if & contains the string "apple1", how can I use & to make the string to "apple2" (i.e replace 1 by 2) ?

jww
  • 97,681
  • 90
  • 411
  • 885
user1418321
  • 1,191
  • 2
  • 9
  • 6

4 Answers4

25

If I guessed right, what you want to do is to apply a subsitution in a pattern matched. You can't do that using &. You want to do this instead:

echo apple1 apple3 apple1 apple2 botemo1 | sed '/apple./ { s/apple1/apple2/g; }'

This means that you want to execute the command substitution only on the lines that matches the pattern /apple./.

Elias Dorneles
  • 22,556
  • 11
  • 85
  • 107
  • Please explain to a longtime sed newbie like me why this is any better than `sed 's/apple1/apple2/g'`. Because I want to use `sed '/apple./ { s/1/2/g; }' ` and it changes it to `botemo2` which is totally counterintuitive. – Steven Lu Feb 06 '16 at 04:46
  • @StevenLu Thing is sed is line-oriented, so `/apple./` means: apply the commands to follow only to the lines that match the pattern. Since `botemo1` is in the same line, sed will apply the substitution for it too. – Elias Dorneles Feb 06 '16 at 12:25
18

You can also use a capture group. A capture is used to grab a part of the match and save it into an auxiliary variable, that is named numerically in the order that the capture appears.

echo apple1 | sed -e 's/\(a\)\(p*\)\(le\)1/\1\2\32/g'

We used three captures:

  1. The first one, stored in \1, contains an "a"
  2. The second one, stored in \2, contains a sequence of "p"s (in the example it contains "pp")
  3. The third one, stored in \3, contains the sequence "le"

Now we can print the replacement using the matches we captured: \1\2\32. Notice that we are using 3 capture values to generate "apple" and then we append a 2. This wont be interpreted as variable \32 because we can only have a total of 9 captures.

Hope this helps =)

  • yes, but this doesn't handle multiple matches in a single line. – jaf0 Jun 18 '14 at 20:00
  • The backreferences are designed for referring to "stored matches" (as the OP phrased); and the question is about replacing a string IN those, not WITH those. – 7heo.tk Apr 30 '15 at 13:01
  • Adding an example to 7heo.tk's comment. You can use back references like so: `sed -e 's/\(a\)\(p*\)\(le\)1 \1\2\32/\1\2\33 \1\2\34/'` will replace `apple1 apple2` with `apple3 apple4`, and it would also work (if you want to) with `appple1 appple2`, resulting in `appple3 appple4`. However, it wouldn't replace `apple1 aple2`, nor `appple1 apple2`, nor `apple1 appple2`, because they don't have the same string stored in the captures (ie. when comparing the words, they're not the same). – Janito Vaqueiro Ferreira Filho Apr 30 '15 at 14:09
  • @JanitoVaqueiroFerreiraFilho I didn't know that you can use the backreferences in the match part of the regex. Do you know if it's POSIX? – 7heo.tk Apr 30 '15 at 15:01
  • @7heo.tk Yes, AFAICT, it's part of BRE (basic regular expressions) that every POSIX capable sed should implement (http://pubs.opengroup.org/onlinepubs/007904975/basedefs/xbd_chap09.html#tag_09_03_06). – Janito Vaqueiro Ferreira Filho Apr 30 '15 at 15:11
  • @JanitoVaqueiroFerreiraFilho I have tested that against GNU and BSD implementations, I have found that in both cases, `sed 's/\(["'"'"']\)\(.*\)\1/\2/g'` actually yields the string between quotes, be them single quotes or double quotes. That's very useful, I'll surely keep that in mind, thank you. – 7heo.tk Apr 30 '15 at 15:19
  • No problem. I like what you did there to match quoted strings. I'll also keep that trick in mind :-) – Janito Vaqueiro Ferreira Filho Apr 30 '15 at 15:24
13

you can first match a pattern and then change the text if matched:

echo "apple1" | sed '/apple/s/1/2/'    # gives you "apple2"

this code changes 1 to 2 in all lines containing apple

none
  • 11,793
  • 9
  • 51
  • 87
3

This might work for you (GNU sed and Bash):

sed 's/apple1/sed "s|1|2|" <<<"&"/e' file
potong
  • 55,640
  • 6
  • 51
  • 83
  • OK, worked it out... Normally the "replacement" string is inserted, but GNU sed uses the `e` flag to execute it and insert its output instead. The executed command passes `&` (the matched pattern) as a here-string to `sed "s|1|2"`, where `|` takes the place of the more conventional `/`. – mwfearnley Sep 30 '18 at 16:14