user1145925
user1145925

Reputation: 1051

Replace multiple lines using sed

I thought I understood sed but I guess not. I have the following two files, in which I want to replace the "why" and "huh" lines with one different line. No whitespace at all.

test.txt:

hi
why
huh
hi
why
huh

test2.txt:

1
hi
why
huh
hi
why
huh

The following two commands give the following results:

sed "N; s/<why\/>\n<huh\/>/yo/g" test.txt > out.txt

out.txt:
hi
why
huh
hi
yo

sed "N; s/<why\/>\n<huh\/>/yo/g" test2.txt > out2.txt

out2.txt:
1
hi
yo
hi
why
huh

What am I not understanding about sed? Why don't both output files contain the following:

hi
yo
hi
yo

Upvotes: 12

Views: 40391

Answers (3)

Cz Zhang
Cz Zhang

Reputation: 41

The reason why it didn't replace both pairs of lines is explained beautifully in brandizzi's answer.

However, if we take one step further. Say we have the following file and we want to replace "apple\njuice" with "pure\nmilk".

test.txt

water water water water 
water water water apple
juice water water apple
juice water water water

The pattern filter way would not work.

sed '/apple/ {N; s/apple\njuice/pure\nmilk/g}' test.txt 

water water water water
water water water pure
milk water water apple
juice water water water

Because the 2nd apple from test.txt, which has been concatenated to the previous line by N, didn't get caught by pattern filter.

One solution I can think of is to use branch to concatenate all lines and then do the replacement.

sed ':a;N;$!ba;s/apple\njuice/pure\nmilk/g' test.txt 

water water water water
water water water pure
milk water water pure
milk water water water

It looks dumb, but I haven't think of a better way yet.

Upvotes: 4

brandizzi
brandizzi

Reputation: 27050

Your expression is almost correct, but it has two problems:

  • If you want to match why as a word, you should put \< and \> around it. You did put just < and \/> around it. So, the first correction is:

    $ sed 'N; s/\<why\>\n\<huh\>/yo/g' test.txt
    
  • But it will not work, either:

    $ sed 'N; s/\<why\>\n\<huh\>/yo/g' test.txt
    hi
    why
    huh
    hi
    yo
    

    Why does it replace only the second pair of lines? Well, in the first line, the N command will concatenate why to hi, leaving in the pattern space the string hi\nwhy. This string is not matched by the s/// command, so the line is just printed. Next time, you have the string huh in the pattern space and concatenate hi to it. Just in the next line you will have why\nhuh in the pattern space to be replaced.

    The solution is to concatenate the next line only when your current line is why, using the address /^why$/:

    $ sed '/^why$/ {N; s/\<why\>\n\<huh\>/yo/g}' test.txt
    hi
    yo
    hi
    yo
    

Upvotes: 13

Birei
Birei

Reputation: 36262

This should work for test.txt file:

sed '/hi/! { N ; s/why\nhuh/yo/ }' test.txt

It means:

When not found hi in a line (it will be why), read next one and substitute all it with yo. Otherwise print directly (when hi).

Output:

hi
yo
hi
yo

Upvotes: 1

Related Questions