【问题标题】:How do I replace multiple occurrence of a character between specified strings using vi or sed?如何使用 vi 或 sed 替换指定字符串之间多次出现的字符?
【发布时间】:2021-03-21 12:36:16
【问题描述】:

我正在尝试替换“,”,仅当它出现在 65510 和“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

所以想要的输出是:

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

我尝试了以下方法:

sed -e 's/,\([^65510]*\)i/_\1i/' input-file.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

【问题讨论】:

  • 您应该在示例输入/输出中包含一些重要的案例,包括 ,165510,foo,i,655101,foo,i,65510,foo,65510,bar,ifoo,65510,i,因为它总是很容易匹配您想要的字符串,但是不匹配您不想要的字符串要困难得多!鉴于其中一些输入(可能还有我没有提到的其他输入),所有当前发布的解决方案都将失败。

标签: awk sed replace between


【解决方案1】:

使用sed,您可以使用循环:

[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

关于t 命令:

tlabel

如果s/// 在读取最后一个输入行和最后一个tT 后成功替换 命令,然后分支到标签;如果 label 被省略,则跳转到脚本末尾。


您可以使用酷炫的 sed 调试工具sedsed 来看看它是如何工作的:

【讨论】:

  • 如果 65510 作为不同字符串的一部分出现,则将失败,例如165510。我知道 OP 没有说清楚,但他们的代码以 /,65510... 开头,所以我怀疑这就是他们真正想要匹配的内容。另请注意,在输出中将i 之前的, 替换为_ 是不受欢迎的。
  • 这是最接近使用“sed”解决我的问题的一个。从现在开始,我可以用“,i”轻松清理“_i”。非常感谢。
  • 埃德,好点。但就我而言,“65510”总是正确的。因此,“,65510”始终出现在整个文件中。 pynexj 的解决方案非常适合我的目的。谢谢。
【解决方案2】:

您能否尝试在 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

更通用的答案:如果我们有不止一次出现 i 并且它可能会或可能不会像 65510....i....i 这样连续出现,那么可以尝试关注。

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

【讨论】:

  • 如果 65510 作为不同字符串的一部分出现,则将失败,例如165510。我知道 OP 没有说清楚,但他们的代码以 /,65510... 开头,所以我怀疑这就是他们真正想要匹配的内容。它还会在输出中将i 之前的, 替换为_
  • @EdMorton,当然,谢谢您的意见,先生。我考虑过 1 个案例,没有考虑过这个极端案例。这就是为什么我通常按照所示示例写行。我会在我完成烹饪和晚餐后尝试涵盖这一点,先生,保重。
【解决方案3】:

这可能对你有用(GNU sed):

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

如果当前行包含65510,然后是i,则将它们之间的任何, 替换为_

【讨论】:

  • @EdMorton 谢谢,修改了解决方案以解决您的问题,实际上我误读了,i 部分,混淆了trieddesired。与往常一样,OP 指定和期望的内容可能会有所不同,所以我总是用 might 这个词来限定我的解决方案。
【解决方案4】:

使用 GNU awk 将第三个 arg 用于 match() 和 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

上面只是将每个输入行分成3部分(直到65510之前的逗号部分,从65510i之前的逗号之前的字符的部分,以及该行的其余部分)然后替换每个,_ 在该中间部分,并将3 个部分重新粘贴在一起以形成修改后的输出行。如果输入行不包含,65510.*,i,则保持原样。

如果,65510x,.*,i(其中x 是任何不是逗号的字符)可以存在于您的输入中,它将失败 - 如果可能发生这种情况,则将其包含在您问题的示例中。

【讨论】:

    【解决方案5】:

    你可以试试 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
    $
    

    $ 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
    $
    

    如果你确定在 65510 之前总会有一个逗号,那么

    $ 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
    $
    

    如果65510两边都用逗号包裹,那么

    $ 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
    $
    

    【讨论】:

      猜你喜欢
      • 2018-06-14
      • 1970-01-01
      • 2020-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多