Lorenzo Gaifas
Lorenzo Gaifas

Reputation: 111

Bash: replacing pattern in specific column but only in lines between two patterns

I have files with this kind of structure:

abc
def
ghi
...
x x y x x
x x z x x
x x y x x
...
JKL
x x y x x
x x z x x
x x y x x
...
...
*empty line*
mno
pqr
...
...

I would like to copy the whole file to a new file but with some changes. Fist, I want to affect only the lines between pattern JKL and the next empty line. On top of that, I need to replace every occurrence of the pattern y with a new pattern NEW, but only if it appears in the third column.

I tried using sed, but I got stuck at how to select columns:

sed -ne '/JKL/,/^$/s/y/NEW/'

this, of course, replaced y with NEW in all columns.

I also tried looking up awk, but I could only find examples of the two separate needs I have, and wasn't able to put them together. How could I do it?

Upvotes: 1

Views: 598

Answers (3)

James Brown
James Brown

Reputation: 37424

Using GNU awk and split(). First some more descriptive test data:

...
JKL
 x x y x x
    x  y  z  x  x

...

Then the script:

$ awk '
/JKL/,/^ *$/ {                 # the desired block 
    n=split($0,a,FS,seps)      # split and store the separators
    b=seps[0]                  # seps[0] has the leading space, init buffer with it
    for(i=1;i<=n;i++) {        # iterate all fields
        if(i==3 && a[i]=="y")  # if 3rd field is y
            a[i]="NEW"         # replace it with with NEW
        b=b a[i] seps[i]       # build the buffer for output
    }
    print b
}' file

and the output:

JKL
 x x NEW x x
    x  y  z  x  x

Upvotes: 0

Sundeep
Sundeep

Reputation: 23677

awk also allows the range syntax similar to sed, see How to select lines between two patterns? for alternate and more flexible ways

awk '/JKL/,/^$/{if($3=="y") $3="NEW"} 1' ip.txt 
  • /JKL/,/^$/ lines of interest
    • if($3=="y") if 3rd field value is exactly the string y
    • $3="NEW" change the 3rd field value to desired text
    • if you need use regex, use sub(/y/, "NEW", $3) or gsub(/y/, "NEW", $3)
  • 1 idiomatic way to print contents of $0

Upvotes: 1

choroba
choroba

Reputation: 241988

Third column is something that follows the beginning of a line, a sequence of non-spaces, a spaces, another sequence of non-spaces, and finally a space:

sed '/^JKL$/,/^$/s/^\([^ ][^ ]* [^ ][^ ]*\) y /\1 NEW /'

or, if your sed supports -r or -E:

sed -E '/^JKL$/,/^$/s/^([^ ]+ [^ ]+) y /\1 NEW /' 

Upvotes: 2

Related Questions