user7091463
user7091463

Reputation: 157

Replace every nth occurrence of a string

Supposing I have a text file. I want to replace every nth occurrence of a string in the text file using sed. If I have a string:

is this just real life or is this just fantasy or is it just me

For every 3rd occurrence of 'is', replacing with 'hat', would give the output

is this just real life or is this just fantasy or hat it just me

I've tried using answers provided on other StackOverflow questions, but none of them work for replacing every occurrence.

Upvotes: 3

Views: 4334

Answers (6)

SYANiDE
SYANiDE

Reputation: 187

It is literally being overthought in other answers. Sed is capable of this, not-so-nearly verbose.

Given a string:

"foo foo foo foo foo"

We can pipe into the following sed expression, targeting the exact occurrence:

sed -e 's/foo/bar/3'

The third occurrence is replaced:

echo "foo foo foo foo foo" | sed -e 's/foo/bar/3'
foo foo bar foo foo

You can target a range by adding an inverse expression as end range, and global replace (g). For example, replace the second through fourth:

echo "foo foo foo foo foo" | sed -e 's/foo/bar/g2' |sed -e 's/bar/foo/g4'
foo bar bar bar foo

This is now kind of sloppy, and can be condensed down to a multiple expression sed statement with the '-r' switch:

echo "foo foo foo foo foo" | sed -re 's/foo/bar/g2' -e 's/bar/foo/g4'
foo bar bar bar foo

The first expression replaces all occurrences of 'foo' with 'bar', starting with the second occurrence of 'foo'. The second expression replaces all occurrence of 'bar' with 'foo', starting with the fourth occurrence of 'bar'.

Version: GNU sed version 4.2.1

Upvotes: 2

potong
potong

Reputation: 58351

This might work for you (GNU sed):

sed -r 's/is/\n&/g;/\n/!b;G;:a;;s/$/#/;s/#{3}$//;/\n$/s/\nis/\nhat/;s/\n//;/\n.*\n/ba;P;s/^.*\n//;h;d' file

I take no kudos for this answer. See here for detailed explanation.

Upvotes: 0

ctac_
ctac_

Reputation: 2471

You can try this gnu sed

sed -E ':A;s/\bis/hat\n/3;x;G;h;s/(.*)\n.*/\1/;x;s/.*\n//;/\bis/bA;x;G;s/\n//g' infile

Upvotes: 1

user2832874
user2832874

Reputation:

If you use sed extended regular expressions (-E instead of -e), you can reformulate the question as follows. Instead of matching "every third occurrence of is", think that you are dealing with longer string match

echo "is this just real life or is this just fantasy or is it just me" | sed -E 's/(is)(.*)(is)(.*)(is)/\1\2\3\4\hat/'

That works on your sample, but it also illustrates that your question is incomplete; do you want to match across lines, or just within the line? Do you want to treat the input buffer as a single long line and replace every third "is" within it, or are you replacing the third "is" on each line? So this example is illustrative but not complete, and the complete answer with sed only will have some other funkiness to it that real code would try to avoid.

The regex method can generate madness and hard to read code. If you can't run awk for some reason, you probably also don't have the -E extended regex flag. If it were me I'd use awk.

Upvotes: 0

Rahul Verma
Rahul Verma

Reputation: 3079

using awk

$ awk '{for(i=1; i<=NF; i++) if($i=="is") if(++count%3==0) $i="hat"}1' file
is this just real life or is this just fantasy or hat it just me

Upvotes: 2

RomanPerekhrest
RomanPerekhrest

Reputation: 92854

awk solution:

awk -v RS='[[:blank:]]+' 'NR % 3 == 0{ $0 = toupper($0) }
                         { printf "%s%s", (NR == 1? "": OFS), $0 }' file

The output:

this this THIS this this THIS this

Upvotes: 0

Related Questions