合并大量文件

合并大量文件

我有 ±10,000 个文件 ( res.1- res.10000),全部由一列和相同数量的行组成。我想要的本质上是简单的;将所有文件按列合并到一个新文件中final.res。我尝试过使用:

paste res.*

然而(虽然这似乎适用于一小部分结果文件,但在整个集合上执行时会出现以下错误:Too many open files.

一定有一个“简单”的方法来完成这个任务,但不幸的是我对 unix 还很陌生。提前致谢!

PS:为了让您了解(我的一个)数据文件是什么样的:

0.5
0.5
0.03825
0.5
10211.0457
10227.8469
-5102.5228
0.0742
3.0944
...

答案1

如果您拥有该计算机的 root 权限,您可以暂时增加“打开文件描述符的最大数量”限制:

ulimit -Hn 10240 # The hard limit
ulimit -Sn 10240 # The soft limit

进而

paste res.* >final.res

之后您可以将其设置回原始值。


A第二种解决方案,如果您无法更改限制:

for f in res.*; do cat final.res | paste - $f >temp; cp temp final.res; done; rm temp

它对paste每个文件调用一次,最后有一个包含所有列的巨大文件(需要一分钟)。

编辑:猫的无用用途...不是

正如评论中提到的,cat这里 ( cat final.res | paste - $f >temp) 的用法并非毫无用处。第一次运行循环时,该文件final.res尚不存在。paste然后就会失败,并且文件永远不会被填充,也不会创建。我的解决方案仅cat在第一次失败,No such file or directorypaste从标准输入中读取一个空文件,但它会继续。该错误可以忽略。

答案2

如果混乱' 答案不适用(因为您没有所需的权限),您可以paste按如下方式批量调用:

ls -1 res.* | split -l 1000 -d - lists
for list in lists*; do paste $(cat $list) > merge${list##lists}; done
paste merge* > final.res

这一次列出了名为 等的文件中的 1000 个文件lists00lists01然后将相应的res.文件粘贴到名为 等的文件中merge00merge01最后合并所有生成的部分合并的文件。

正如所提到的混乱您可以增加一次使用的文件数量;限制是给定的值ulimit -n减去您已经打开的文件数量,所以您会说

ls -1 res.* | split -l $(($(ulimit -n)-10)) -d - lists

使用限制减十。

如果您的版本split不支持-d,您可以将其删除:它所做的只是告诉split您使用数字后缀。默认情况下,后缀将为aa, abetc. 而不是01, 02etc。

如果有太多文件ls -1 res.*失败(“参数列表太长”),您可以将其替换为find以避免该错误:

find . -maxdepth 1 -type f -name res.\* | split -l 1000 -d - lists

(正如指出的唐克里斯斯蒂,当管道输出-1时不需要;ls但我将其保留以处理ls别名为 的情况-C。)

答案3

尝试以这种方式执行它:

ls res.*|xargs paste >final.res

您还可以将批次分成几部分,然后尝试以下操作:

paste `echo res.{1..100}` >final.100
paste `echo res.{101..200}` >final.200
...

最后合并最终文件

paste final.* >final.res

答案4

考虑到涉及的文件数量、行大小等,我认为它将超过工具的默认大小(awk、sed、paste、*等)

我会为此创建一个小程序,它既不会打开 10,000 个文件,也不会打开数十万行的长度(10,000 个文件,每行 10 个(示例中行的最大大小))。它只需要一个约 10,000 个整数数组,来存储从每个文件读取的字节数。缺点是它只有一个文件描述符,它被每个文件、每一行重复使用,这可能会很慢。

FILES和的定义ROWS应更改为实际的精确值。输出被发送到标准输出。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define FILES 10000 /* number of files */
#define ROWS 500    /* number of rows  */

int main() {
   int positions[FILES + 1];
   FILE *file;
   int r, f;
   char filename[100];
   size_t linesize = 100;
   char *line = (char *) malloc(linesize * sizeof(char));

   for (f = 1; f <= FILES; positions[f++] = 0); /* sets the initial positions to zero */

   for (r = 1; r <= ROWS; ++r) {
      for (f = 1; f <= FILES; ++f) {
         sprintf(filename, "res.%d", f);                  /* creates the name of the current file */
         file = fopen(filename, "r");                     /* opens the current file */
         fseek(file, positions[f], SEEK_SET);             /* set position from the saved one */
         positions[f] += getline(&line, &linesize, file); /* reads line and saves the new position */
         line[strlen(line) - 1] = 0;                      /* removes the newline */
         printf("%s ", line);                             /* prints in the standard ouput, and a single space */
         fclose(file);                                    /* closes the current file */
      }
      printf("\n");  /* after getting the line from each file, prints a new line to standard output */
   }
}

相关内容