我在一个文件夹中有数亿个小的纯文本文件。我想将它们合并到一个大文件中并进行一些处理。最快的方法是什么?目前,我有以下代码:
#!/bin/bash
FOLDER="some-folder"
TARGET="target-file"
FILES=`find $FOLDER -name "*.txt"`
for f in $FILES
do
cat $f | ./some-processing-script.pl >> $TARGET
done
虽然这适用于少量文件。当它实际用于处理大量文件时,在目标文件大于25G左右后变得非常慢。我认为是因为cat ... >> $TARGET
,在将新内容附加到目标的末尾之前,必须扫描每个新输入文件的整个当前目标文件。
我知道如何使用java或python来解决这个问题。我很好奇我是否能在bash中解决这个问题。感谢。
答案 0 :(得分:5)
您可以改为重定向循环的输出:
for f in $FILES
do
./some-processing-script.pl < $f
done >> $TARGET
(我也删除了useless use of cat。)
答案 1 :(得分:4)
您对“合并”的定义是什么?您的Perl脚本是否采用文件名参数?您的文件名是否包含空格或其他尴尬的字符?
假设您的脚本不接受命令行参数,您可以执行以下操作之一:
cat $(find $FOLDER -name "*.txt") | ./some-processing-script.pl >$TARGET
或者,如果单个命令行的文件太多,则:
find $FOLDER -name "*.txt" -print | xargs cat | ./some-processing-script.pl > $TARGET
或者,如果您的名称中有空格且足够现代find
(符合POSIX 2008):
find $FOLDER -name "*.txt" -exec cat {} + | ./some-processing-script.pl > $TARGET
如果您确实要始终附加到目标文件,请将>
替换为>>
。在这些示例中,cat
用于连接多个文件,这是cat
的正确用法。 UUOC(无用cat
)奖励使用cat
来处理单个文件,而I / O重定向将在此时处理。
如果您负责Perl脚本,则应将其修改为“标准过滤器”,该程序读取命令行中指定的文件或标准输入(如果未指定文件)。然后,您可以删除cat
:
./some-processing-script.pl $(find $FOLDER -name "*.txt") >$TARGET
find $FOLDER -name "*.txt" -print | xargs ./some-processing-script.pl > $TARGET
find $FOLDER -name "*.txt" -exec ./some-processing-script.pl {} + > $TARGET
在这些选项中,最后一个是我使用的选项,假设Perl脚本接受或可以修改为接受命令行上的文件名。如果Perl脚本不能(可以)处理命令行参数,那么我将使用带有cat
和管道的第三个命令。但这说明了为什么你应该设计程序(Perl脚本)在可能的情况下表现得像标准的Unix过滤器;这意味着您可以更有效地将它们组合到命令管道中。
答案 2 :(得分:0)
如果您可以修改perl
脚本,我建议您使用:
shopt -s globstar # enable bash4 recursion with **
./some-processing-script.pl **/*.txt > big_file.txt
perl
脚本:
while (<>) {
# processing the content
print;
}
您也可以这样做:
find $FOLDER -name "*.txt" -exec cat {} + |
./some-processing-script.pl > big_file.txt
答案 3 :(得分:0)
我在目录中的23k文件上尝试了这行bash代码,我认为速度可以接受。
for f in path/*.txt; do cat "${f}" >> merged.txt; done
参考:https://www.unix.com/shell-programming-and-scripting/148505-concatenation-large-number-files.html
答案 4 :(得分:-1)
使用命令行开关:
perl -pe '{}' abc_file_qualifiier_*.csv > merged_file.csv
-p
假定您的脚本周围有输入循环。线条被打印出来。
-e
用于输入脚本的一行
'{}'
一个空脚本