使用 shell 脚本查找文件中重复的文本块

使用 shell 脚本查找文件中重复的文本块

假设我有一个包含以下几行的文本文件:-

abcd/efgh/a.jar
{
abcd/efgh/a.class
cdef/ghij/b.class
klmn/opqr/c.class
}
lkmn/opqr/b.zip
{
abcd/efgh/a.class
cdef/ghij/b.class
}
abcd/efgh/a.jar
{
cdef/ghij/b.class
}

现在,第一种情况下的 abcd/efgh/a.jar 在花括号内有 abcd/efgh/a.class、cdef/ghij/b.class 和 klmn/opqr/c.class。将其视为 1 个文本块。现在,下面的 abcd/efgh/a.jar 再次将 cdef/ghij/b.class 放在大括号内。我想删除这部分/文本块。所以最终的输出需要是这样的:-

abcd/efgh/a.jar
{
abcd/efgh/a.class
cdef/ghij/b.class
klmn/opqr/c.class
}
lkmn/opqr/b.zip
{
abcd/efgh/a.class
cdef/ghij/b.class
}

任何帮助将不胜感激:)

答案1

使用

for i in `awk '/}/ {if (NR!=1) print "";next} \
                {printf "%s ",$0,"}"}END{print ""}' yt.txt \
        |awk '{print $1}'|sort|uniq \
    `; \
    do \
        awk '/}/ {if (NR!=1) print "";next} \
            {printf "%s ",$0,"}"}END{printf ""} \
            ' yt.txt \
         |grep "$i"|sed 's/ /\n/g'|grep -v "$i"|sort|uniq \
            |awk -v var="$i" ' NR==1 {printf var} {print $0} END {print "}"}'  \
    ;done \

下面 1 行中的相同命令(用于复制目的)

for i in `awk '/}/ {if (NR!=1) print "";next} {printf "%s ",$0,"}"}END{print ""}' yt.txt|awk '{print $1}'|sort|uniq` ; do awk '/}/ {if (NR!=1) print "";next} {printf "%s ",$0,"}"}END{printf ""}' yt.txt|grep "$i"|sed 's/ /\n/g'|grep -v "$i"|sort|uniq|awk -v var="$i" ' NR==1 {printf var} {print $0} END {print "}"}' ;done

解释:

for部分将返回块的唯一标题 ( abcd/efgh/a.jar, lkmn/opqr/b.zip) 并将其传递给do块。该do部分将首先显示grep每个标题的所有行,其中也包括重复项。然后它将排除标题并合并该标题块下的所有剩余行,然后在第一行添加标题。并}在最后进行硬编码。

例子

bash-4.2$ cat yt.txt
abcd/efgh/a.jar
{
abcd/efgh/a.class
cdef/ghij/b.class
klmn/opqr/c.class
}
lkmn/opqr/b.zip
{
abcd/efgh/a.class
cdef/ghij/b.class
}
abcd/efgh/a.jar
{
cdef/ghij/b.class
d.class
}



bash-4.2$ for i in `awk '/}/ {if (NR!=1) print "";next} {printf "%s ",$0,"}"} \
> END{print ""}' yt.txt |awk '{print $1}'|sort|uniq` \
> ; do awk '/}/ {if (NR!=1) print "";next} {printf "%s ",$0,"}"}END{printf ""}' yt.txt \
>  |grep "$i"|sed 's/ /\n/g'|grep -v "$i"|sort|uniq \
> |awk -v var="$i" ' NR==1 {printf var} {print $0} END {print "}"}'\
> ;done
abcd/efgh/a.jar
{
abcd/efgh/a.class
cdef/ghij/b.class
d.class
klmn/opqr/c.class
}
lkmn/opqr/b.zip
{
abcd/efgh/a.class
cdef/ghij/b.class
}

答案2

for在我看到使用带有awkandsortuniqand的循环的解决方案后grepsed我尝试了使用一个工具而不是六个工具的解决方案:

sed ':a
  N;$!ba
  y/\n_/_\n/;s/^/_/
  :b
  s/\(_[^_]*_{\)\([^}]*\)\(_[^_}]*\)\(_[^}]*\)\(_}.*\)\1\([^}]*\)\3_/\1\2\3\4\5\1\6_/;tb
  :c
  s/\(_[^_]*_{\)\([^}]*\)_}\(.*\)\1\([^}]*\)_}/\1\2\4_}\3/;tc
  s/^_//
  y/\n_/_\n/' yourfile

可以完成这项工作,但我必须承认正则表达式编写比阅读更容易...... (-;

答案3

perl -alF'/\n[}{]\n/' -0777ne '
   for ( 0 .. $#F/2 ) {
      my $i = 2*$_;
      my($k,$v) = @F[$i,$i+1];
      if ( exists $h{$k} ) {
         $h{$k} .= join $\, grep { ! exists $seen{$k,$_} } split $\, $v;
      } else {
         push @k, $k;
         $seen{$k,$_}++ for split $\, $h{$k} = $v;
      }
   }
   print "$_\n{\n$h{$_}\n}" for @k;
' yourfile

结果

abcd/efgh/a.jar
{
abcd/efgh/a.class
cdef/ghij/b.class
klmn/opqr/c.class
}
lkmn/opqr/b.zip
{
abcd/efgh/a.class
cdef/ghij/b.class
}

在职的

输入文件被消化,然后根据选项提到的字段分隔符分割成字段-F。我们将在数组中获得偶数个元素@F。然后偶数编号作为哈希的键,%h同时它们对应。值是从下一个奇数中获取的。

%h通过在记录分隔符 ($\ = \n) 上拆分奇数元素来填充哈希。同时,我们将键放入数组中,@k以便我们可以按照遇到的顺序检索哈希元素。

一直以来,只使用那些尚未见过的奇怪元素。

相关内容