Reputation: 27
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
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
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
Reputation: 70957
sed
linesed '/^\*LOG 3/,/old string/s/old string/new string/' test.in
Translated:
From
*LOG 3
tonext line containing *old string*
, replaceold string
bynew string
Upvotes: 4
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