【问题标题】:How to delete mirror lines of a text file using bash如何使用 bash 删除文本文件的镜像行
【发布时间】:2020-09-16 18:11:19
【问题描述】:

这是文件:

a b
c d
d c
b a

第 1 行与第 4 行镜像,第 2 行与第 3 行镜像。我只想保留其中一条镜像线。这意味着我想删除第 3 行和第 4 行。这只是一个示例。我想要的是有一个代码,它计算出镜像线并只保留其中一条。

【问题讨论】:

  • 你的文件有多大,字段的输出顺序是否重要,只要集合是不同的?可以有a a 行吗?
  • mirror lines 可能意味着不同的东西,例如字段顺序和每个字段中的字符顺序等 - ab cdcd abdc ba 的“镜像”还是其他?如果您的真正要求不仅仅是按任一顺序处理 2 个字段,那么 edit 您的问题会更清楚。

标签: bash shell unix awk mirror


【解决方案1】:

常见的惯用 awk 方法是始终以特定顺序排列字段,而不管其输入顺序如何,并将结果用作唯一性测试数组键,例如在您的示例中只有 2 个字段:

$ awk '!seen[$1>$2 ? $1 FS $2 : $2 FS $1]++' file
a b
c d

【讨论】:

    【解决方案2】:

    试试下面的命令: 这仅适用于 GNU awk

    awk '{split ($0, ln, " "); asort( ln ); for( i = 1; i <= length(ln); i++ ) printf("%s ", ln[i] ); printf( "\n" )}' file.txt | sort -u
    

    说明: 在这里,我将线条水平排序,所以:

    • split($0, line, " ") --> 将行拆分成一个名为“ln”的数组
    • asort( ln ) --> 将对这个数组进行排序
    • for( i = 1; i 这用于打印输出,但它会在单行中。
    • printf( "\n" ) --> 与上面部分一起使用,使输出行分开
    • 以上部分将对文件进行水平排序,而 sort -u 将只保留唯一值。

    【讨论】:

      【解决方案3】:

      这些适用于作为字符的行,这意味着不同数量的字段和不同长度的字段,不仅适用于提供的示例。

      第一种解决方案

      awk -F "" '{r=""; for (i=NF;i;i--) r=r $i} !seen[$0]++ && !seen[r]++' file
      

      上述命令不包括对称线,例如:a a。我们可以像这样包含它们(打印一次):

      awk -F "" '{r=""; for (i=NF;i;i--) r=r $i} !seen[$0]++ && (!seen[r]++ || $0==r)' file
      

      或进一步改进,采用 Ed Morton 的 this answer 的想法,使用一个哈希并始终保留两个字符串中较大的一个(这是按字母顺序比较)。

      awk -F "" '{r=""; for (i=NF;i;i--) r=r $i} !seen[$0>r? $0: r]++' file
      

      第二个解决方案

      rev file | paste -d \\n file - | awk '!seen[$0]++ && NR%2'
      

      这里使用换行符将文件及其镜像粘贴,结果镜像为偶数行,仅打印奇数行的唯一性(但对所有行都进行了测试),awk 条件的顺序很重要,因为我们想要@987654327 @ 也适用于偶数行。

      【讨论】:

        【解决方案4】:

        一个可能需要很长时间才能运行的快速破解 -

        declare -A item=()
        while read -a l
        do x="$(printf "%s\n" "${l[@]}"|sort|tr '\n' ' ')"
           item["$x"]=1
        done < file
        printf "%s\n" "${!item[@]}" | sort
        a b
        c d
        

        文件越大和/或更复杂,运行速度越慢,使用的内存就越多。请改用 Homer 的awk 解决方案。

        【讨论】:

          【解决方案5】:

          首先存储文件。在一个数组中是值,另一个是行号。
          使用相反的线再次运行并寻找相等的值。
          当反转的值在原始集合中时,您知道镜像具有相同的行号。你可以从原来的集合中删除镜像,知道你刚刚发现反向的值也在集合中。

          awk 'NR==FNR{a[$0]=FNR;b[NR]=$0; next}
               $0 in a {delete a[b[FNR]]; }
               END { for (i in a) { print i } }' inputfile <(rev inputfile)
          

          举例说明:
          当您使用示例文件运行它时

          a b
          c d
          d c
          b a
          

          读取第一个文件后的数组如下所示

          a[a b]=1, a[c d]=2, a[d c]=3, a[b a]=4
          b[1]=a b, b[2]=c d, b[3]=d c, b[4]=b a
          

          现在处理反向文件,找到第一条记录'b a',所以它必须有一个镜像。但是文件是逆序的原文件,所以b a的镜像必须在同一行号。
          要删除a[a b],您可以使用b[1] 找到索引。

          【讨论】:

            【解决方案6】:

            另一个 awk:

            $ awk '
            !(($1,$2) in a) && !(($2,$1) in a) {    # if neither version is found in hash a
                print                               # output
                a[$1,$2]                            # and hash
            }' file
            

            输出:

            a b
            c d
            

            编辑:另外一个版本可以反转字符串,可以处理ab cddc ba等字符串:

            awk '
            function rev(str,    rts) {        # string reversing function
                for(i=1;i<=length();i++)
                    rts=substr(str,i,1) rts
                return rts
            }
            !($0 in a) && !(rev($0) in a) {    # if neither version is found in hash a
                print                          # output
                a[$0]                          # and hash
            }' file
            

            【讨论】:

            • 不确定这是 OP 想要的:它只适用于只有两个字段的行。 ab cddc ba 的组合也失败了。
            • 是的,样本不是很明确。
            【解决方案7】:

            这结合了 Ed Morton 和 Homer 的答案,使其可以在任意数量的列上运行,同时在 (真的是gawk)中运行。只会打印第一次出现,它也使用 GNU asort 扩展:

            awk 'function mkkey(line){
                res="";
                split(line, a);
                asort(a);
                for(i=1; i<=length(a); ++i) res = res FS a[i];
                return res
            } !seen[mkkey($0)]++' file
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2010-09-25
              • 2011-08-21
              • 1970-01-01
              • 2022-01-27
              • 2012-05-14
              • 2021-09-04
              • 1970-01-01
              • 2016-05-25
              相关资源
              最近更新 更多