我在 Linux 机器上有一个巨大的文件(大约 100Gb),其中第二列中包含重复的子字符串。例子:
92957ea93f634985;02a2a09322bbbb2d894c;acfb4aa85f577db320d5a0701210238f 62be40ee38d3d62e;1f433e74c9498f051bca;4ad1905e8ff598e9ea5b71c0e48424d9 08356a4e6e5edc25;5d41a09322bbbb2d894c;d92ef5610121033f34dd881b4b910820 c5a2cd0c4e2191e3;83fdc9498f051bcab9e8;5bcb136cfd3326br9d1f52ce7537b901 1e3a1f877316966d;12ff3e74c9498f051bca;a9547b3db00e821bf5e8db900121038e 00c5a857928fbfaf;547b3db00e821b1604eh;11919d5616e4306x4a495118f52c41d4 92ea3e74c9498f04;8faca09322bbbb2d894c;10256a9ff1787f483db00e862119030a
我期望以下输出:
92957ea93f634985;02a2a09322bbbb2d894c;acfb4aa85f577db320d5a0701210238f 62be40ee38d3d62e;1f433e74c9498f051bca;4ad1905e8ff598e9ea5b71c0e48424d9 08356a4e6e5edc25;5d41a09322bbbb2d894c;d92ef5610121033f34dd881b4b910820 1e3a1f877316966d;12ff3e74c9498f051bca;a9547b3db00e821bf5e8db900121038e 92ea3e74c9498f04;8faca09322bbbb2d894c;10256a9ff1787f483db00e862119030a
(行顺序并不重要)
我尝试使用以下代码创建一个较小的测试文件:
awk -F";" '!_[substr($2,5,16)]++' test.csv
但它只显示第一次出现,我需要保存所有这些。因为它是一个巨大的文件,我无法使用意味着加载到内存中的解决方案,所以我认为上面的代码不好。我只需要保存上面定义的第 2 列中包含相同子字符串值的行,因此我们将不胜感激。
编辑:犯了一个错误。上面的代码不起作用。它显示第一个重复项,因为我输错了分隔符。如果正确的话,它会显示文件的全部内容。
答案1
awk
如果要测试的子字符串约为每行的 20%,并且您的文件为 100GB,则典型的awk
解决方案可能需要高达 20GB 的内存。该解决方案需要对文件进行两遍解析(或者将所有行存储在内存中,这会更糟),首先将子字符串存储在关联数组中,同时计算它们的出现次数,然后打印任何不唯一的内容。
awk 'FNR==NR{seen[substr($0,22,16)]++; next} seen[substr($0,22,16)] > 1' file file
但在你的情况下,这个关联数组可能不适合内存。
种类
对于处理内存的解决方案,您可以使用sort
.它将仅使用可用内存,如有必要,它将使用临时文件并对它们进行合并排序。它可能会慢一些,但即使在内存较低的情况下也可以做到。
LC_ALL=C sort -k1.22,1.37 file | uniq -D -s21 -w16
LC_ALL=C
如果您的文件包含非 UTF-8 多字节字符,您可以删除,并且您可能还需要为sort
命令定义不同的 tmp 位置,-T, --temporary-directory=DIR
指向具有 100GB 可用空间和所需权限的某个位置,以防您/tmp
没有 100 GB免费(有时/
分区很小)
上面的代码uniq
将跳过前 21 个字符并测试其后的前 16 个字符的唯一性。-D
将仅打印重复的行。与此类似,上面的sort
命令正在测试每一行的相同固定字符范围。
仅提取重复的子字符串 & awk
根据您的数据,您可以尝试这个解决方案,结合上面的想法并尝试适应内存并更快,实际上这是awk
适合内存的解决方案。这个命令:
cut -c22-37 file | sort | uniq -d > subs.txt
将提取在文件中指定固定位置出现两次或多次的子字符串。它只会将它们每个打印一次到subs.txt
.请注意, 的大小subs.txt
将小于第一个解决方案中关联数组的大小,因为已排除唯一值。
现在,如果 的大小subs.txt
足够小并且适合内存(这取决于重复频率),您只能解析该文件一次:
awk 'FNR==NR{seen[$0]; next} (substr($0,22,16) in seen)' subs.txt file >> output
或者,您可以将此文件拆分为 N 个部分,使用split -l
并运行上述命令来解析该文件 N 次,每次都附加到同一个输出文件。根据 的大小subs.txt
,如果这可以在 1-2 步内完成,我认为它会比总排序解决方案更快。