Reputation: 157
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
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
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
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
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
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
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