f4r7
f4r7

Reputation: 27

awk/sed find then find again from that point

I have a file where I want to find and replace a very specific line. I have provided a simplified file below which I'll call test.in. If I were to do this manually I would find "log 3" then I would find "old string" from that point and replace it with "new string". In practice the files are large with lots of information between LOG number and old string, but these are the unique identifiers.

*LOG 1

old string
old string

*LOG 2

old string
old string
old string

*LOG 3

old string [Want to replace this with "new string"]


*LOG 4

old string
old string

I thought about using the following:

  grep -A 700 "*LOG 3" test.in | sed '0,/old string/{s/old string/new string/}'

But this doesn't work for me as I need to correct the entire file. I also came across the following on these pages:

awk '/old string/{count++;if(count==3){sub("old string","new string")}}1' test.in

However this also doesn't work as it assumes I know how many "old string" there are before I get to the one I need to replace - and in practice I don't as there are many different files.

I cannot simply find LOG 3 with a line space and old string and replace it that way, as there is a ton of different information between LOG 3 and old string.

Is there a simple way I can find something then search again from that new position, and then use sed/awk and save the entire file.

Many thanks :)

Upvotes: 1

Views: 175

Answers (4)

Ed Morton
Ed Morton

Reputation: 204259

This is probably what you want:

$ awk '/^\*LOG/{f=($NF==3)} f{sub(/old string/,"new string")} 1' file
*LOG 1

old string
old string

*LOG 2

old string
old string
old string

*LOG 3

new string [Want to replace this with "new string"]


*LOG 4

old string
old string

Note that although the text you're searching for and replacing has the word "string" in it, the above and any sed solution or any other awk solution posted so far is NOT doing a literal string search/replace. Instead it's searching for a regexp and replacing it with a backreference-enabled string so it'll misbehave given various metacharacters. If you REALLY want to do a literal string search/replace then it's:

$ awk '
    BEGIN { old="old string"; new="new string" }
    /^\*LOG/ { f=($NF==3) }
    f && s=index($0,old) { $0=substr($0,1,s-1) new substr($0,s+length(old)) }
1' file
*LOG 1

old string
old string

*LOG 2

old string
old string
old string

*LOG 3

new string [Want to replace this with "new string"]


*LOG 4

old string
old string

Upvotes: 1

kvantour
kvantour

Reputation: 26521

With awk you could do the same as in the (sed solution from F. Hauri](https://stackoverflow.com/a/59582010/8344060)

awk '/^[*]LOG 3$/,/old_string/{sub(/old_string/,"new_string")}1' file

This is, however, not really flexible if you want to start doing other things with your awk. The flag method is, therefore, the easiest:

awk '/^[*]LOG 3$/{f=1};f{f=!(sub(/old_string/,"new_string"))}; 1' file

Upvotes: 3

F. Hauri  - Give Up GitHub
F. Hauri - Give Up GitHub

Reputation: 70957

Simply 1 sed line

sed '/^\*LOG 3/,/old string/s/old string/new string/' test.in

Translated:

From *LOG 3 to next line containing *old string*, replace old string by new string

Upvotes: 4

Matt
Matt

Reputation: 15186

Not sure how it's done with sed, but with vim (ex/vi too?) you can simply paste regexps one after another:

vim '+/^\*LOG 3//^old string/s/.*/new string' '+wq' test.txt

Upvotes: 0

Related Questions