ファイルから重複した行を繰り返し削除しますが、1行は保持してファイル内の行を一意にします。

ファイルから重複した行を繰り返し削除しますが、1行は保持してファイル内の行を一意にします。

フォルダが多く、フォルダにファイルが含まれています。単一ファイルまたは複数のファイルに同じ行が複数回表示されることがあります。ファイルがソートされません。したがって、一部の行は複数のファイルで繰り返され、これらのファイルは別のフォルダにあります。

重複した行を削除し、すべてのファイルでそれらの1つだけを維持したいと思います。ファイル構造と名前も同じにしてください。

私は試しましたが、すべてのファイルではなく、各ファイルでのみ一意にしました。このコードは各ファイルの行を一意にし、ファイル名を保持します。

for i in $(find . -type f); do
    awk '!seen[$0]++' "$i" > tmp_file
    mv ./tmp_file "$i"
done

Q:ファイル構造と名前を維持しながら、すべてのサブフォルダ内のすべてのファイルで行を一意にするにはどうすればよいですか?

以下は私のファイルの例です。簡単にするために、ここにはファイルのみをリストし、ファイルは同じフォルダまたは別のフォルダにあります。

入力する:

$ cat File-1
1
2
3
1

$ cat File-2
2
3
4
1

$ cat File-3
2
4
5
6

出力:

$ cat File-1
1
2
3

$ cat File-2
4

$ cat File-3
5
6

私の場合、その行の最初の出現を維持することをお勧めしますが、必須ではありません(保持された行はすべてのファイルに存在する可能性があります)。

答え1

#!/usr/bin/perl
use File::Find;
my $headdir="/some/path";
my @files=();
my $lines={};
find( { wanted => sub { push @files, $_ }, no_chdir => 1 }, $headdir );
foreach my $file (@files) {
  next unless(-f $file);
  system "cp $file $file". ".old";
  open(my $fhin, "$file".".old");
  open(my $fhout, ">$file");
  while(<$fhin>) {
    if(not defined $lines->{$_}) {
      print $fhout $_;
      $lines->{$_} = 1;
    }
  }
  close($fhin);
  close($fhout);
  #optional: system("rm $file".".old");
}

編集する:質問に記載されているファイルでのみテストするには、コードを少し変更する必要があります。

答え2

次の操作は、処理するファイルの数が一度だけfind実行できるほど小さい場合にのみ機能します。awkまた、ファイルツリー全体をコピーできると仮定します(つまり、ストレージスペースに制限はありません)。

ファイルツリーがorigディレクトリにあるとします。

$ cp -pr orig tmp
$ cd tmp
$ find . -type f -exec awk '
  BEGIN { print ARGC }
  FILENAME != fn {
    close( "../orig/"fn )
    printf "" > ( "../orig/"FILENAME )
  }
  !seen[$0]++ { print > ( "../orig/"FILENAME ) }
  { fn = FILENAME; }' {} +

結果に満足したら、作業を進めるだけですrm -r tmp

print ARGCawk通話回数を表示するために使用されます。ARGCコマンドライン引数配列(スクリプト自体を含む)の要素数。複数回印刷すると、グローバル行重複排除が失敗したことを意味します。
(実際に処理するファイルの総数を数えることができる場合は、複数回呼び出す場合は、ファイルが変更されないようにブロックを変更するだけです。if ( (ARGC - 1) < total_number_of_files) exitawk

関連情報