シェルスクリプトを使用してファイル内の重複したテキストブロックを見つける

シェルスクリプトを使用してファイル内の重複したテキストブロックを見つける

次の行を含むテキストファイルがあるとします。

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.jarlkmn/opqr/b.zipdodogrep}

はい

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

forawkandsortとandを含むuniqループを使用するソリューションを見た後、6つのツールの代わりに1つのツールを使用してソリューションを試しました。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ハッシュ要素が見つかった順序で検索できるように、キーを配列に配置します。

これまでまだ見たことのない奇妙な要素だけを使っています。

関連情報