递归地从文件中删除重复的行,但保留一行 - 使行在文件中唯一

递归地从文件中删除重复的行,但保留一行 - 使行在文件中唯一

我有很多文件夹,文件夹中包含文件。同一行可能会在单个文件和/或多个文件中出现多次。文件未排序。因此,有些行在多个文件中重复,并且这些文件位于不同的文件夹中。

我想删除重复的行并在所有文件中仅保留其中一行。文件结构和名称也应保持相同。

我已经尝试过,但仅在每个文件中而不是在所有文件中使唯一。此代码使每个文件中的行都是唯一的并保留文件名:

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要多次调用时不会修改任何文件)。

相关内容