【问题标题】:How to replace a duplicate string with counter appended values in multiple files in linux command line如何在linux命令行中的多个文件中用计数器附加值替换重复的字符串
【发布时间】:2018-06-04 10:56:16
【问题描述】:

我们如何通过将字符串"string" 替换为计数器递增值来修改现有文件,如下所示。

注意1:第一个"string" 被跳过。 注意2:“字符串”也不会在一行中出现超过一次。 注意3: foo "string"bar -> foo "string"1bar 是正确的

File1("string" 在文件中的任意行出现一次)

some text
"string" here

File2("string" 在文件中的任何一行出现三次)

some text
"string" here
some more
text "string"
why "string"

File3("string" 不会出现在文件内的任何行)

some text
why here
some more>
text pttn
why pttn

File4("string" 在文件内的任意行出现一次)

some "string"
no here

如何将"string" 替换为"string"1 "string"2"string"3 等?

预期输出:

文件1

some text
"string" here

文件2

some text
"string"1 here
some more
text "string"2
why "string"3

文件3

some text
why here
some more
text pttn
why pttn

文件4

some "string"4
no here

【问题讨论】:

  • 图案每行可以出现多次吗?
  • @Bishwas Mishra,在得到人们的正确答案后,尝试选择一个正确的答案来关闭线程。
  • 不,模式每行不会出现超过一次。
  • 是的@RavinderSingh13 我正在尝试答案。
  • @EdMorton 我已经更新了这个问题。我所说的pattern 仅指用双引号括起来的"string"。谢谢你告诉。

标签: regex awk sed


【解决方案1】:

您的问题仍然不清楚,但这可能是您正在寻找的:

$ awk -v str='"string"' '
    BEGIN { lgth = length(str) }
    pos=index($0,str) {
        $0 = substr($0,1,pos+lgth-1) cnt substr($0,pos+lgth)
        cnt++
    }
    1' file{1,2,3,4}
some text
"string" here
some text
"string"1 here
some more
text "string"2
why "string"3
some text
why here
some more
text pttn
why pttn
some "string"4
no here

只需添加-i inplace(使用 GNU awk)即可更改输入文件而不是打印输出。以上假设您需要一个文字字符串匹配,并且该字符串不需要通过空格、标点符号或其他任何内容与其他文本分隔。

【讨论】:

  • 我得到的输出不符合预期。
  • 我得到以下输出(请注意一行中有两个单词): some text "string" here0 some text "string"1 here1 some more text "string"22 为什么是 "string"33 some text 为什么在这里 some more> text pttn 为什么pttn some "string "44 不在这里
  • 你是对的。该代码有效。但是什么是 -i,我应该在哪里添加它?
  • 我说添加-i inplace,而不仅仅是-i,而且这个页面上还有2个其他答案已经在使用它,所以我很惊讶你不得不问:awk -i inplace ...
  • 谢谢,这是赏金开始后的唯一答案。它正确且非常足智多谋。
【解决方案2】:

未经测试

gawk -i inplace -v p="pattern" '
    {for (i=1; i<=NF; i++) if ($i == p) {$i = p n; n++}; print}
' File{1,2,3,4}

【讨论】:

    【解决方案3】:

    如果您想将输出保存到 Input_file(s) 本身,以下可能会对您有所帮助。

    gawk -i inplace -v INPLACE_SUFFIX=.bak -v val="-1" '/pattern/{val++} {val=val==0?"":val;sub(/pattern/,"&"val)} 1' File1 File2 File3 File4
    

    现在也添加非单线形式的解决方案。

    gawk -i inplace -v INPLACE_SUFFIX=.bak -v val="-1" '
    /pattern/{
      val++}
    {
      val=val==0?"":val;
      sub(/pattern/,"&"val)}
    1' File1 File2 File3 File4
    

    【讨论】:

    • 如果你改变你的两个动作的顺序,你可以摆脱三元运算。即{ sub ...}/pattern/{val++}。最初 Val 是一个空字符串,你会再次找到模式,因为你只是将它与数字连接起来。
    • 你也可以只做/pattern/{ sub(/pattern/,"&amp;"val); val++}1。它可能会快一点。
    【解决方案4】:

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

    sed -nr '/"string"/!b;x;/./!{s/^/0/;x;ba};:b;s/9(_*)$/_\1/;tb;s/^(_*)$/0\1/;s/$/\n0123456789/;s/(.)(_*)\n.*\1(.).*/\3\2/;y/_/0/;x;G;s/("string")(.*)\n(.*)/\1\3\2/;:a;W /dev/stdout' File? |
    sed -i.bak -e '/"string"/!b;R /dev/stdin' -e 'd' File?
    

    创建一个包含所有修改过的字符串的文件,更新文件,每次使用修改过的字符串。

    第一次调用 sed 获取包含"string" 的每一行并将其递增(第一行除外)并将其输出到stdout

    第二次调用 sed 将包含 "string" 的每一行替换为来自 stdin 的下一行。这些文件通过-i.bak 选项内联更新,这会创建以.bak 为后缀的原始文件的备份。

    注意这假设每行只出现一次"string"

    【讨论】:

      【解决方案5】:

      我只是想试试这个。

      这是一个单行:

      awk -v strVar='"string"' -v count=-1 '
         NR>1 { $0 ~ strVar && ++count && gsub(strVar, strVar count) }
      1' file1 file2 file3 file4
      

      你本质上:

      1. 初始化一个计数器,(-v count=-1)
      2. 忽略第一行输入(NR&gt;1
      3. 检查该行是否与字符串匹配 ($0 ~ strVar)
      4. 如果确实增加了计数器 (&amp;&amp; ++count)
      5. 如果增加的计数器大于零(++count 将返回 0 -&gt; false 用于零值和 &gt;0 -&gt; true 从那时起,因此它也可以作为打印条件正常工作)
        • 开始用递增的后缀替换字符串 (&amp;&amp; gsub(str, str count)
      6. 打印结果 ({}1)

      请注意,如果您不介意也计算第一行,和/或知道 file1 的第一行不包含 "string",那么解决方案可以更小/更简单:

      awk -v strVar='"string"' -v count=1 '
         { gsub(strVar, strVar count) && count++ }
      1' file1 file2 file3 file4
      

      这意味着对于每个替换,您都会增加计数器,并会输出:

      % awk -v strVar='"string"' -v count=1 '{ gsub(strVar, strVar count) && count++ }1' file1 file2 file3 file4
      
      some text
      "string"1 here
      
      some text
      "string"2 here
      some more
      text "string"3
      why "string"4
      
      some text
      why here
      some more
      text pttn
      why pttn
      
      some "string"5
      no here
      

      问候

      【讨论】:

      • 在给定各种输入和变量值的情况下会失败,而$0 ~/str/ 不会 check if the line matches the string,它会检查该行是否与正则表达式str 匹配,即使它匹配str 中包含的正则表达式。如果您想检查匹配正则表达式"string",那么那将是$0 ~ str,而不是$0 ~ /str/,但您仍在进行正则表达式匹配,而不是字符串匹配,因此将所有内容命名为str和“string”非常误导(另外,这不是 OP 正在寻找的 - 他想要字符串比较)。 str""count 中的 "" 绝对没有做任何有用的事情。
      • @EdMorton 我考虑了您的评论来修改我的解决方案。我 1)将 str 重命名为 strVar,2)更改匹配运算符以不将其作为文字处理,3)删除无用"" 4) 进一步简化了条件和 5) 相应地修改了解释。我不确定我是否明白你关于 OP 想要字符串比较的最后一点。如果您指的是"string" 可能是另一个字符串的子集,那么对strVar='^"string"| "string" |"string$' 进行简单的正则表达式修改可能会成功,不是吗?如果您有更多反馈,请告诉我。
      • 你有一些你正在调用的字符串,但没有将其视为字符串,而是将其视为正则表达式 - 这是非常具有误导性的。 awk 中的字符串比较是使用 ==inindex() 完成的,而不是使用 ~gsub()。如果您想将某些内容视为正则表达式,请将其命名为 regexp 并在其上使用正则表达式运算符,如果您希望将某些内容视为字符串,则将其命名为字符串并在其上使用字符串运算符。不要命名字符串,然后在其上使用正则表达式运算符,反之亦然。
      猜你喜欢
      • 2019-01-16
      • 2018-06-05
      • 2018-04-19
      • 1970-01-01
      • 2010-11-17
      • 2014-01-03
      • 2011-08-14
      • 2017-04-01
      相关资源
      最近更新 更多