파일에서 중복된 줄을 반복적으로 제거하되 한 줄은 유지하여 파일에서 줄을 고유하게 만듭니다.

파일에서 중복된 줄을 반복적으로 제거하되 한 줄은 유지하여 파일에서 줄을 고유하게 만듭니다.

폴더가 많고 폴더에 파일이 포함되어 있습니다. 단일 파일 및/또는 여러 파일에서 동일한 줄이 여러 번 나타날 수 있습니다. 파일이 정렬되지 않습니다. 따라서 일부 줄은 여러 파일에서 반복되며 이러한 파일은 다른 폴더에 있습니다.

중복된 줄을 제거하고 모든 파일에서 그 중 하나만 유지하고 싶습니다. 파일 구조와 이름도 동일하게 유지되어야 합니다.

나는 시도했지만 모든 파일이 아닌 각 파일에서만 고유하게 만들었습니다. 이 코드는 각 파일에서 행을 고유하게 만들고 파일 이름을 유지합니다.

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

질문: 파일 구조와 이름을 유지하면서 모든 하위 폴더의 모든 파일에서 행을 고유하게 만들려면 어떻게 해야 합니까?

다음은 내 파일의 예입니다. 단순화를 위해 여기에는 파일만 나열하고 파일은 동일하거나 다른 폴더에 있습니다.

입력하다:

$ 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) exit)awk

관련 정보