manoj
manoj

Reputation: 31

sed find and replace for only specific number of occurrences

I have the following problem.

I have a file with sequence and I want to find a specific pattern and replace it with another pattern, but only for a specific number of times.

eg:

ABCDEFGHIJKLMN
ABCDEFGHIJKLMN
ABCDEFGHIJKLMN
ABCDEFGHIJKLMN
ABCDEFGHIJKLMN
ABCDEFGHIJKLMN

I want to find "DEFGHI" and replace it with "ABCDEF" for only 3 times.

sed -i 's/DEFGHI/ABCDEF/g' /home/user/test.txt

I don't want to replace globally; instead I want to replace only 3 times.

Upvotes: 1

Views: 2715

Answers (3)

Lars Fischer
Lars Fischer

Reputation: 10149

This can be done using bash and GNU sed like this:

sed -z -i "$(echo s/DEFGHI/ABCDEF/{3..1}\;)" yourfile
  • The {3..1} results in the the s part repeated three time with flag 3 to 1. Each s command replaces only the third, second and first occurence. The inverted order is necessary, so that the counting and replacing do not mix up.
  • -z takes all the input lines at once, thus the counting is not per line but per file.

For your reference, here is what the command substitution around the echo produces:

echo s/DEFGHI/ABCDEF/{3..1}\;
> s/DEFGHI/ABCDEF/3; s/DEFGHI/ABCDEF/2; s/DEFGHI/ABCDEF/1;

Upvotes: 2

zdim
zdim

Reputation: 66883

This replaces the given pattern three times, whichever way they're distributed over lines

perl -0777 -pe'$i += s/DEF/xxx/ while $i < 3' < data.txt  > out.txt

It assumes that the pattern is not broken over lines. It makes passes over the string, searching from the beginning each time. This comes with a caveat explained below and is ineffcient but it is simple.

If the replacement itself recreates the pattern when combined with the surrounding text, this will also be replaced in the next pass. Such a replacement would have to contain parts of the pattern itself and in a specific way. Still, if this is a concern let me know.

Handling of possible overlapping patterns is not specified in the problem. If there are any this solution will replace the first so in the next pass the overlapping one will have been gone.

Upvotes: 2

SLePort
SLePort

Reputation: 15461

You can replace the first occurrence found three times:

sed -i '0,/DEFGHI/s//ABCDEF/;0,/DEFGHI/s//ABCDEF/;0,/DEFGHI/s//ABCDEF/' file

output:

ABCABCDEFJKLMN
ABCABCDEFJKLMN
ABCABCDEFJKLMN
ABCDEFGHIJKLMN
ABCDEFGHIJKLMN
ABCDEFGHIJKLMN

Upvotes: 0

Related Questions