Ozzie Ghani
Ozzie Ghani

Reputation: 203

Replace string starting with in between two lines using bash

Is there a way to use sed to replace starting of the string in the entire file without using a loop?

For example, my source data is the following:

str_address: 123 main street
str_desc: Apt3
str_desc: 2nd floor
str_city: new york
str_desc: mailing address

Now, the file will have thousands of addresses, but I want anytime "str_desc" appears after "str_address" and before "str_city" to be replaced with "str_address", however any "str_desc" that appears after str_city to remain unchanged.

Desired output:

str_address: 123 main street
str_address: Apt3
str_address: 2nd floor
str_city: new york
str_desc: mailing address

I can extract this info with,

cat file | awk '/str_city/{f=0} f; /str_address/{f=1}'

which gives

str_desc: Apt3
str_desc: 2nd floor

But I am having trouble changing the first "str_desc" to "str_address".

Upvotes: 1

Views: 133

Answers (2)

Benjamin W.
Benjamin W.

Reputation: 52112

You can use an address range in sed:

$ sed '/str_address/,/str_city/s/str_desc/str_address/' infile
str_address: 123 main street
str_address: Apt3
str_address: 2nd floor
str_city: new york
str_desc: mailing address

This leaves all the str_desc outside of the /str_address/,/str_city/ range untouched, and substitutes the others with str_address (that's the s/str_desc/str_address/ part).

Upvotes: 1

paxdiablo
paxdiablo

Reputation: 881153

You almost have the complete solution in your awk extraction code:

awk '/str_city/{f=0} f; /str_address/{f=1}'

The idea is to:

  • turn the flag on when you see str_address.
  • turn the flag off when you see str_city.
  • replace str_desc with str_address if the flag is on.

That's basically (in readable form, and the order is important):

awk '
    $1 == "str_address:"           { flag = 1 }
    $1 == "str_desc:" && flag == 1 { $1 = "str_address:" }
    $1 == "str_city:"              { flag = 0 }
                                   { print }
    ' < inputFile >outputFile

Here's a transcript showing it in action:

pax$ echo '
     str_address: 123 main street
     str_desc: Apt3
     str_desc: 2nd floor
     str_city: new york
     str_desc: mailing address
     ' | awk '
         $1 == "str_address:"           { flag = 1 }
         $1 == "str_desc:" && flag == 1 { $1 = "str_address:" }
         $1 == "str_city:"              { flag = 0 }
                                        { print }'

str_address: 123 main street
str_address: Apt3
str_address: 2nd floor
str_city: new york
str_desc: mailing address

And, of course, a minified version:

awk '$1=="str_address:"{f=1}$1=="str_desc:"&&f==1{$1="str_address:"}$1=="str_city:"{f=0}{print}' < inputFile >outputFile

Upvotes: 2

Related Questions