How do I replace multiple occurrence of a character between specified strings using vi or sed?

Viewed 125

I'm trying to replace "," only when it occurs between 65510 and "i":

2001::/32,fd00::230:5,0,100,0,65510,6939,i

2001:200:900::/40,fd00::230:5,0,100,0,65510,6939,2516,7660,7660,7660,i

So the desired output is:

2001::/32,fd00::230:5,0,100,0,65510_6939,i

2001:200:900::/40,fd00::230:5,0,100,0,65510_6939_2516_7660_7660_7660,i

I've tried the following:

sed -e 's/,\([^65510]*\)i/_\1i/' input-file.txt

But the output only replace the last ",":

2001::/32,fd00::230:5,0,100,0,65510,6939_i

2001:200:900::/40,fd00::230:5,0,100,0,65510,6939,2516,7660,7660,7660_i
5 Answers

Could you please try following, written and tested with shown samples in GNU awk.

awk '
match($0,/65510.*i/){
  val=substr($0,RSTART,RLENGTH)
  gsub(/,/,"_",val)
  print substr($0,1,RSTART-1) val substr($0,RSTART+RLENGTH)
}
' Input_file

More Generic answer: In case we have more than one occurrences of i and it may or may not come in in series like 65510....i....i then one could try following.

awk '
{
  line=""
  while(match($0,/65510[^i]*/)){
    val=substr($0,RSTART,RLENGTH+1)
    gsub(/,/,"_",val)
    line=(line?line:"")substr($0,1,RSTART-1) val
    $0=substr($0,RSTART+RLENGTH+1)
  }
  if($0!=""){
    line=line $0
  }
  print line
}
'  Input_file

This might work for you (GNU sed):

sed -E '/\b65510\b.*i/{:a;s/\b(65510\b[^,i]*),([^i])/\1_\2/;ta}' file

If the current line contains 65510 and then i, replace any ,'s between them by _'s.

With sed you can use a loop:

[STEP 101] $ cat file
2001::/32,fd00::230:5,0,100,0,65510,6939,i
2001:200:900::/40,fd00::230:5,0,100,0,65510,6939,2516,7660,7660,7660,i
[STEP 102] $
[STEP 103] $ sed -e :loop -e 's/\(,65510.*\),\(.*,i\)/\1_\2/; tloop' file
2001::/32,fd00::230:5,0,100,0,65510_6939,i
2001:200:900::/40,fd00::230:5,0,100,0,65510_6939_2516_7660_7660_7660,i
[STEP 104] $
[STEP 105] $ # if your sed supports -E it can be a bit simpler --
[STEP 106] $ sed -E -e :loop -e 's/(,65510.*),(.*,i)/\1_\2/; tloop' file
2001::/32,fd00::230:5,0,100,0,65510_6939,i
2001:200:900::/40,fd00::230:5,0,100,0,65510_6939_2516_7660_7660_7660,i

About the t command:

tlabel

If a s/// has done a successful substitution since the last input line was read and since the last t or T command, then branch to label; if label is omitted, branch to end of script.


You can use the cool sed debug tool sedsed to see how it works:

sedsed

With GNU awk for the 3rd arg to match() and gensub():

$ awk 'match($0,/(.*,)(65510.*)(,i.*)/,a){$0=a[1] gensub(/,/,"_","g",a[2]) a[3]} 1' file
2001::/32,fd00::230:5,0,100,0,65510_6939,i

2001:200:900::/40,fd00::230:5,0,100,0,65510_6939_2516_7660_7660_7660,i

The above just separates each input line into 3 parts (the part til the comma before 65510, the part from 65510 til the character before the comma before i, and the rest of the line) then replaces every , with _ in that middle part and pastes the 3 parts back together to form the modified output line. If the input line doesn't contain ,65510.*,i then it's left as-is.

It will fail if ,65510x,.*,i (where x is any char that is not a comma) can exist in your input - if that can happen then include it in the example in your question.

You can try Perl

$ cat lou.txt
2001::/32,fd00::230:5,0,100,0,65510,6939,i
2001:200:900::/40,fd00::230:5,0,100,0,65510,6939,2516,7660,7660,7660,i
$ perl -pe ' s/65510\K(.+?)(?=,i)/$c=$1;$c=~s!,!_!g;$c/ge ' lou.txt
2001::/32,fd00::230:5,0,100,0,65510_6939,i
2001:200:900::/40,fd00::230:5,0,100,0,65510_6939_2516_7660_7660_7660,i
$

or

$ perl -pe ' s/(?=65510)(.+?)(?=,i)/$c=$1;$c=~s!,!_!g;$c/ge ' lou.txt
2001::/32,fd00::230:5,0,100,0,65510_6939,i
2001:200:900::/40,fd00::230:5,0,100,0,65510_6939_2516_7660_7660_7660,i
$

If you are sure that there will always be a comma before 65510, then

$ perl -pe ' s/,(?=65510)(.+?)(?=,i)/$c=$1;$c=~s!,!_!g;sprintf(",%s",$c)/ge ' lou.txt
2001::/32,fd00::230:5,0,100,0,65510_6939,i
2001:200:900::/40,fd00::230:5,0,100,0,65510_6939_2516_7660_7660_7660,i
$

If 65510 is wrapped both sides by comma, then

$ perl -pe ' s/,(?=65510,)(.+?)(?=,i)/$c=$1;$c=~s!,!_!g;sprintf(",%s",$c)/ge ' lou.txt
2001::/32,fd00::230:5,0,100,0,65510_6939,i
2001:200:900::/40,fd00::230:5,0,100,0,65510_6939_2516_7660_7660_7660,i
$
Related