1

How to write a sed (or awk, or both) which will take the following:

echo '1 aa 2 2 3 bb 5 bb 2 5' | sed/awk ...

And only replace the n-th occurrence of a string? For example the 3rd occurrence of 2 or the second occurrence of bb?

So the expected output would be (when replacing 2nd occurrence of bb with replaced for example):

1 aa 2 2 3 bb 5 replaced 2 5

The input string, the replacement string, and n can be any arbitrary input.

4 Answers4

8

Using sed to change the second occurance of bb

$ sed 's/bb/new-bb/2' file
1 aa 2 2 3 bb 5 new-bb 2 5

or to change the third occurance of 2

$ sed 's/2/12/3' file
1 aa 2 2 3 bb 5 bb 12 5
sseLtaH
  • 2,786
4

To change the nth occurrence within a line, sed has syntax for that with s/pattern/replacement/3 as already shown by @HatLess.

To change the nth occurrence in the whole input, you could use perl with:

perl -pse 'if($n>0) {s{pattern}{--$n ? $& : "replacement"}ge}' -- -n=3
2

With awk you could do:

awk -v str='bb' -v pos=2 -v rplc='newBB' '
 {
   strPos=0
   for(i=1; i<=NF; i++)
       if($i==str && (++strPos==pos) ) { $i=rplc; break }
 }1' infile

Output:

1 aa 2 2 3 bb 5 newBB 2 5

Note: Above it assumes that there is only a sinlge character as the field separator (defualt: Space or Tab) since otherwise consecutive field separator will squeeze into one or you can force awk to match on each single field separator separately (so the FS must be forced as regex mode):

awk -F'[\t]' -v OFS='\t' -v str='bb' -v pos=2 -v rplc='newBB' '
 {
   strPos=0
   for(i=1; i<=NF; i++)
      if($i==str && (++strPos==pos) ) { $i=rplc; break }
 }1' infile

Note: change \t to Space character if the field seperator is Space chartacter.


To change the nth occurrence in the whole input (yet I assumed field seperator is the Tab character):

awk -F'[\t]' -v OFS='\t' -v str='bb' -v pos=2 -v rplc='newBB' '
 strPos!=2{
            for(i=1; i<=NF; i++)
                if($i==str && (++strPos==pos) ) { $i=rplc; break }
 }1' infile
αғsнιη
  • 41,407
2

We can do both with sed in a single command:

echo "1 aa 2 2 3 bb 5 bb 2 5"|sed -e "s/2/kop/3" -e "s/bb/pp/2"

This command will replace 3rd occurrence of 2 with kop and 2nd occurrence of bb with pp.