Ubuntu – How to replace every nth occurrence of pattern using sed ONLY

awkbashsed

I have searched all over the web but i can't find any good solutions. I have read on many forums that sed is not the tool (but awk is) for this but it makes no sense to me why. I mean you can do a simple substitution for occurrence number 5 for example like: sed 's/pattern/replace/5' to replace only the 5th instance. Why can't sed do 's/pattern/replace/<every 5th occur>' for one line? Isn't that a basic function..

Anyway my problem is that our client's software runs on a windows environment that has limited linux functions and unfortunately awk is not available on the system. Installing new commands is NOT an option for the company for whatever reason.

So my question is, what is the best way to use sed and/or mixed with bash for replacing every nth occurrence of a pattern per line? I only have one line anyway as i already stripped all the new line characters. Is bash for loop the only way?

Best Answer

Isn't that a basic function..

Obviously, it isn't. But it's still possible:

sed 's/a\([^a]*\($\|a[^a]*\($\|a[^a]*\($\|a[^a]*\($\|a\)\)\)\)\)/A\1/g'

More readable with -r where we can drop the backslashes:

sed -r 's/a([^a]*($|a[^a]*($|a[^a]*($|a[^a]*($|a)))))/A\1/g' 

i.e. a, followed by any number of non-a, followed by either end of line, or another a, followed by any number of non-a, etc. Everything after the first a is remembered in \1.

BTW, if Perl is available, you can use:

perl -pe 'BEGIN { @A = qw(a A) } s/a/$A[not $i++ % 5]/g'

% is the modulo operator. $i is incremented with each match, negation of the modulo is 1, 0, 0, 0, 0, 1, etc., which used as the index into @A picks the correct a or A.