如何在bash中有效地将大量文件合并到一个文件中?

时间:2012-11-10 06:31:53

标签: bash shell

我在一个文件夹中有数亿个小的纯文本文件。我想将它们合并到一个大文件中并进行一些处理。最快的方法是什么?目前,我有以下代码:


#!/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中解决这个问题。感谢。

5 个答案:

答案 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用于输入脚本的一行 '{}'一个空脚本