-5

Using GNU sed, I try to replace first occurrence of pattern in file, but I don't want to replace if there is another pattern before the match.

For example, if the file contains line with "bird [number]" I want to replace the number with "0" if this pattern has no "cat" word any where before.

Example text

dog cat - fish bird 123
dog fish - bird 1234567
dog - cat fish, lion bird 3456

Expected result:

dog cat - fish bird 123
dog fish - bird 0
dog - cat fish, lion bird 3456

I try to combine How to use sed to replace only the first occurrence in a file? and Sed regex and substring negation solutions and came up with something like

sed -E '0,/cat.*bird +[0-9]+/b;/(bird +)[0-9]+/ s//\10/'

where 0,/cat.*bird +[0-9]+/b;/(bird +)[0-9]+/ should match the first occurrence of (bird +)[0-9]+ if the cat.*bird +[0-9]+ pattern does not match, but I get

dog cat - fish bird 123
dog fish - bird 0
dog - cat fish, lion bird 0

The third line is also changed. How can I prevent it? I think it is related to address ranges, but I do not get it how to negate the second part of the address range.

Ryszard Czech
  • 18,032
  • 4
  • 24
  • 37

2 Answers2

2

This might work for you (GNU sed):

sed '/\<cat\>.*\<bird\>/b;s/\<\(bird\) \+[0-9]\+/\1 0/;T;:a;n;ba' file

If a line contains the word cat before the word bird end processing for that line. Try to substitute the number following the word bird by zero. If not successful end processing for that line. Otherwise read/print all following lines until the end of the file.

Might also be written:

sed -E '/cat.*bird/b;/(bird +)[0-9]+/{s//\10/;:a;n;ba}' file
potong
  • 55,640
  • 6
  • 51
  • 83
  • Thank you, it works as expected. However, I wonder if `;T` is really necessary here, I see it works without it. – Ryszard Czech May 20 '19 at 14:59
  • 1
    @RyszardCzech it may well work without the `T` command, but that depends on your data. You only want the first match to be replaced not every match. After the first match the remainder of the file is left as is. – potong May 20 '19 at 21:32
-1

sed is for doing simple s/old/new replacements, that is all. For anything else just use awk, e.g. with GNU awk instead of the GNU sed you were using:

$ awk 'match($0,/(.*bird\s+)[0-9]+(.*)/,a) && (a[1] !~ /cat/) {$0=a[1] 0 a[2]} 1' file
dog cat - fish bird 123
dog fish - bird 0
dog - cat fish, lion bird 3456
Ed Morton
  • 188,023
  • 17
  • 78
  • 185