Martin Vegter
Martin Vegter

Reputation: 527

sed: replace pattern only if followed by empty line

I need to replace a pattern in a file, only if it is followed by an empty line. Suppose I have following file:

test
test

test

...

the following command would replace all occurrences of test with xxx

cat file | sed 's/test/xxx/g'

but I need to only replace test if next line is empty. I have tried matching a hex code, but that doesn ot work:

cat file | sed 's/test\x0a/xxx/g'

The desired output should look like this:

test
xxx

xxx

...

Upvotes: 0

Views: 1844

Answers (3)

grebneke
grebneke

Reputation: 4494

Suggested solutions for sed, perl and awk:

sed

sed -rn '1h;1!H;${g;s/test([^\n]*\n\n)/xxx\1/g;p;}' file

I got the idea from sed multiline search and replace. Basically slurp the entire file into sed's hold space and do global replacement on the whole chunk at once.

perl

$ perl -00 -pe 's/test(?=[^\n]*\n\n)$/xxx/m' file

-00 triggers paragraph mode which makes perl read chunks separated by one or several empty lines (just what OP is looking for). Positive look ahead (?=) to anchor substitution to the last line of the chunk.

Caveat: -00 will squash multiple empty lines into single empty lines.

awk

$ awk 'NR==1 {l=$0; next}
       /^$/ {gsub(/test/,"xxx", l)}
       {print l; l=$0}
       END {print l}' file

Basically store previous line in l, substitute pattern in l if current line is empty. Print l. Finally print the very last line.

Output in all three cases

test
xxx

xxx

...

Upvotes: 3

potong
potong

Reputation: 58430

This might work for you (GNU sed):

sed -r '$!N;s/test(\n\s*)$/xxx\1/;P;D' file

Keep a window of 2 lines throughout the length of the file and if the second line is empty and the first line contains the pattern then make a substitution.

Upvotes: 2

BMW
BMW

Reputation: 45243

Using sed

sed -r ':a;$!{N;ba};s/test([^\n]*\n(\n|$))/xxx\1/g'

explanation

:a                    # set label a
$ !{                  # if not end of file
    N                 # Add a newline to the pattern space, then append the next line of input to the pattern space
    b a               # Unconditionally branch to label. The label may be omitted, in which case the next cycle is started. 
}
#  simply, above  command :a;$!{N;ba} is used to read the whole file into pattern.
s/test([^\n]*\n(\n|$))/xxx\1/g   # replace the key word if next line is empty (\n\n) or end of line ($)

Upvotes: 2

Related Questions