【问题标题】:How to print lines with duplicated fields?如何打印具有重复字段的行?
【发布时间】:2019-08-05 18:52:48
【问题描述】:

我需要打印带有重复字段的行,尝试使用 sed 它不起作用。
输入文件有两行:

s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u1
s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0

输出应该只是第二行,因为它有完全重复的字符串(字段)。
但它使用以下命令打印两行

sed -rn '/(\b\w+\b).*\b\1\b/ p' input_file

谢谢
RKP

【问题讨论】:

  • 第一行:s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u1
  • 第二行:s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0
  • 请将该示例输入的所需输出添加到您的问题中。
  • 第一行有很多重复的字符串。字符串“s”是重复的,是字符串“s1”和“s1/”和“s2/s3”。要让计算机做你想做的事,你必须准确地描述你想要什么。
  • 请指定字段分隔符。是 / 还是 空格 还是什么?

标签: awk sed


【解决方案1】:

我能从你的问题中看出你所需要的就是:

$ awk '$1==$3' file
s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0

如果这不是您所需要的,请更新您的问题以提供更具代表性的示例输入/输出。

【讨论】:

    【解决方案2】:

    [@BenjaminW。正确地观察到我稍微误读了这个问题。我的答案留在下面以供参考,但我将其撤回作为该问题的候选答案。]

    这就是你想要的:

    sort input_file | uniq -d
    

    sort 命令对输入文件的内容进行排序,以便在排序后,相同的行会彼此相邻。 uniq 命令通常会折叠重复的行,但是当使用 -d 选项调用时,只会打印重复的行。

    当然,我的解决方案只有在不需要使用sed 时才可接受。

    【讨论】:

    • 我认为问题在于识别包含重复字符串的行,而不是与其他行重复的行。
    【解决方案3】:

    您可以使用awk 来做到这一点:

    awk '{for(i=1;i<NF;i++)for(j=i+1;j<=NF;j++)if($i==$j){print;next}}' input_file
    

    它不限于 3 列,无论重复发生在哪里。

    如果你想要相反,打印没有重复的行:

    awk '{for(i=1;i<NF;i++)for(j=i+1;j<=NF;j++)if($i==$j)next; print}'
    

    【讨论】:

      【解决方案4】:

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

      sed -E 'h;s/\s*(\S+)\s*/\n\1\n/g;/(\n[^\n]+\n).*\1/!d;g' file
      

      在保留空间中复制当前行。

      用换行符替换非空白字符串两侧的任何空白。

      如果没有重复,则删除掺假行。

      否则,用保留空间中原始行的副本替换模式空间并打印。

      【讨论】:

        【解决方案5】:

        添加只有 1 个循环的 GENERIC 解决方案。因此,这将查找完整行中是否有任何 2 个字段相同(如果您不想硬编码字段编号,则很方便)。

        awk '{delete a;for(i=1;i<=NF;i++){if(++a[$i]>1){print;next}}}'  Input_file
        

        您显示的示例输出将如下所示。

        s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0
        

        说明:现在为上述代码添加详细说明。

        awk '                           ##Starting awk program here.
        {                               ##Starting main BLOCK here.
          delete a
          for(i=1;i<=NF;i++){           ##Starting a for loop which runs from i=1 to till value of NF here, where NF is out of the box variable of awk.
            if(++a[$i]>1){              ##Checking condition if value of array a whose index is $1 is greater than 1 here, if yes then run following.
              print                     ##Printing current line now, as per OP if 2 fields are equal line should be printed.
              next                      ##Using next keyword for skipping all further statements and skipping basically for loop to save time if a match is found then NO need to run it further.
            }                           ##Closing BLOCK for if condition.
          }                             ##Closing BLOCK for fopr loop here.
        }                               ##Closing main BLOCK here.
        '   Input_file                  ##Mentioning Input_file name here.
        

        【讨论】:

        • @Raj KP,请您检查一下我的 GENERIC 解决方案,如果这对您有帮助,请告诉我。
        • 关联数组的好用法 ;-)
        • 实际上你的解决方案不起作用......因为你没有删除print之后和next之前的关联数组的内容......你应该添加一个delete a。如果你尝试在我的input 上运行你的命令,它会打印太多...
        • @Allan,谢谢你,Allan,非常好的收获,我最初是这样做的(先写一些其他的解决方案)后来我忘记了,感谢你的帮助,干杯 :)
        • 其实可以进一步压缩,条件为if(a[$i]++)。不过想法不错。
        【解决方案6】:

        输入:

        $ cat input
        a b c
        s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0
        1 2 3
        a b c
        a b b
        a a
        1
        

        命令:

        awk '{for(i=1;i<=NF-1;i++)for(j=i+1;j<=NF;j++)if($i == $j){print; next}}' input
        

        输出:

        s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0
        a b b
        a a
        

        说明:

        RavinderSingh13 的解决方案在复杂性方面更好,但使用更多内存,因为需要将所有行值保存在关联数组中。

        {
                for (i = 1; i <= NF - 1; i++) { #outer loop to from 1 to NF-1
                        for (j = i + 1; j <= NF; j++) { #inner loop from i+1
                                if ($i == $j) { #value comparison of the two elements selected
                                        print $0 #print
                                        next    #jump to next line
                                }
                        }
                }
        }
        

        【讨论】:

          【解决方案7】:

          如果-P 可用,则使用grep 或使用perl

          $ cat ip.txt
          s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u1
          s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0
          2.5 42 32.5 abc
          3.14 3.14 123
          part cop par
          
          $ grep -P '(?<!\S)(\S++).*(?<!\S)\1(?!\S)' ip.txt
          s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0
          3.14 3.14 123
          
          $ perl -ne 'print if /(?<!\S)(\S++).*(?<!\S)\1(?!\S)/' ip.txt
          s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0
          3.14 3.14 123
          
          • (?&lt;!\S) 没有非空白字符的断言
          • (\S++) 捕获所有非空白字符,所有格量词确保部分字段不会匹配
          • .* 任意数量的中间字符
          • (?&lt;!\S)\1(?!\S) 匹配整个字段,非空白字符的礼貌环顾断言

          【讨论】:

            【解决方案8】:

            使用 Perl - 正则表达式和反向引用

            perl -nle ' print if /(?:^|\s)(\S+)\s+.*?(?<=\s)\1(?:\s+|$)/ms ' file
            

            感谢 @Sundeep 发现微妙的问题,感谢 @zdim 帮助修复它

            以下输入

            $ cat  input
            a b c
            s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u1
            s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0
            1 2 3
            a b c
            a b b
            a a
            1
            2.5 42 32.5 abc
            part cop par
            spar cop par
            
            $ perl -nle ' print if /(?:^|\s)(\S+)\s+.*?(?<=\s)\1(?:\s+|$)/ms ' input
            s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0
            a b b
            a a
            
            $
            

            另一种使用hash/lookbehind的方法

            $ perl -lane ' %k=/(\S+)(?<=(.))/g ; print if scalar(@F) != scalar(keys %k) ' input
            s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0
            a b b
            a a
            
            $
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-02-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多