Village
Village

Reputation: 24423

How to find and replace the first n appearances with sed?

I have been using this code, to replace the first occurrence of a pattern in a file:

sed "0,\#pattern#s##replacement#" input.txt > output.txt

To replace the first 5 occurrences of a pattern in a file, I used this:

sed "0,\#pattern#s##replacement#" input.txt > output1.txt
sed "0,\#pattern#s##replacement#" output1.txt > output2.txt
sed "0,\#pattern#s##replacement#" output2.txt > output3.txt
sed "0,\#pattern#s##replacement#" output3.txt > output4.txt
sed "0,\#pattern#s##replacement#" output4.txt > output5.txt
mv output5.txt output.txt

How can I more simply replace the first n appearances of a match with sed?

Upvotes: 0

Views: 155

Answers (3)

ooga
ooga

Reputation: 15501

sed has no natural counting ability. You could do something weird like this.

sed -r '
  /pattern/ {
    s//replacement/
    x
    s/.*/&x/ 
    t clear
    : clear
    s/x{3}/&/
    x
    T
    q
  }
' file

In the last substitute command, set the number of x's to the number of substitutions you want. In practice this command would need to be used with -i to modify the file in place since it quits and stops processing the file after it performs the requested number of substitutions.

The above with comments:

sed -r '
  /pattern/ {
    s//replacement/
    x          # exchange spaces
    s/.*/&x/   # increment number of x s
    t clear    # reset the successul substitition flag
    : clear    
    s/x{3}/&/  # test for the required number of x s to quit
    x          # exchange spaces
    T          # if substitution was not successful, next cycle
    q          # quit
  }
' file

Here's another idea, but it can only go up to 10 replacements.

sed -r '
  1{x;s/^/0/;x}
  /pattern/ {
    s//replacement/
    x
    y/0123456789/1234567890/
    tx :x   # reset substitution flag
    s/3/&/
    x; T; q
}'

If you're using a sed that doesn't have the T command, it can be replaced with it's opposite, the t command. Change:

T
q

to

t quit
b
: quit
q

Upvotes: 2

rici
rici

Reputation: 241881

This is not really a natural job for sed (personally I'd used awk) but the following should work:

 sed '1{x;s/.*/00000/;x};/PATTERN/{x;s/0//;x;t1;b;:1;s/PATTERN/REPLACEMENT/}'

The length of the string of 0s in the first s command is the number of replacements; I used 0 with the intention of generating the sed command as follows:

 sed "1{x;s/.*/$(printf %*d $COUNT 0)/;x};
     /$PATTERN/{x;s/0//;x;t1;b;:1;s/$PATTERN/$REPLACEMENT/}"

With Gnu sed, you can replace t1;b;:1; with T;

Here's the awk version:

 awk 'N&&sub(PAT,REPL){N--};1' N=5 PAT="pattern" REPL="replacement"

Upvotes: 4

perreal
perreal

Reputation: 98088

You can also consider perl:

perl -lpe 's#pattern#s##replacement# and $n++ if $n < 5' input

Upvotes: 1

Related Questions