我有一个场景,其中有我的三个大文件Test.txt
,Test1.txt
并且Test2.txt
有以下详细信息。
H|||||||||||||||||||||||
D||||||||||||||||||||||||
D|||||||||||||||||||||||
H|||||||||||||||||||||
D||||||||||||||||||||||||
D||||||||||||||||||||||||
T||||||||||||||||||||||||
我必须删除除 D 行之外的所有内容。在我的所有三个文件中,它应该如下所示。(超过 10 GB)
D||||||||||||||||||||||||
D|||||||||||||||||||||||
D||||||||||||||||||||||||
D||||||||||||||||||||||||
Test.txt
因此,在仅保留、Test2.txt
和中的 D 行后Test3.txt
,我必须将它们合并到新文件中。
我已经使用 sed 完成了上述操作。
sed '/^\('D'\)|/!d' $Filename.txt >> $NewFilename.txt
但由于文件巨大,需要很长时间。
我们可以使用任何其他命令以有效的方式完成此操作吗?
答案1
cat Test.txt Test2.txt Test3.txt | LC_ALL=C grep '^D' > newfile.txt
或者:
for file in Test.txt Test2.txt Test3.txt; do
LC_ALL=C grep '^D' < "$file"
done > newfile.txt
或者,如果您grep
喜欢的 GNUgrep
支持该-h
选项(以避免打印文件名):
LC_ALL=C grep -h '^D' Test.txt Test2.txt Test3.txt > newfile.txt
通过使用LC_ALL=C
我们可以避免grep
尝试解析 UTF-8 数据。通过使用^D
,grep
将仅查看每行的第一个字符。grep
,尤其grep
是 GNU 通常比sed
.
答案2
这很可能是 I/O 绑定操作(而不是 CPU 绑定),因此即使您不使用如下的正则表达式引擎:
grep -F 'D|' Test.txt Test2.txt Test3.txt
这将需要很长时间,因为文件必须被标记为行,然后扫描模式。
如果这是一次性的事情并且您不介意编码,您可以将mmap(3)
整个文件放入内存并使用memmem(3)
:
char *p;
if ((p = memmem(file, size, "\nD|", 3)) != NULL) {
/* massage the line, i.e. find the next '\n'
* and print the region between p+1 and the
* next '\n' */
}
其中file
是指向映射缓冲区的指针,size
是文件大小。 (如果这有帮助,我很乐意详细说明)。
这种方法仍然需要一些时间(因为您的问题是 I/O 限制),但至少您可以节省将文件标记为行的时间。