我有 ±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 directory
并paste
从标准输入中读取一个空文件,但它会继续。该错误可以忽略。
答案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 个文件lists00
,lists01
然后将相应的res.
文件粘贴到名为 等的文件中merge00
,merge01
最后合并所有生成的部分合并的文件。
正如所提到的混乱您可以增加一次使用的文件数量;限制是给定的值ulimit -n
减去您已经打开的文件数量,所以您会说
ls -1 res.* | split -l $(($(ulimit -n)-10)) -d - lists
使用限制减十。
如果您的版本split
不支持-d
,您可以将其删除:它所做的只是告诉split
您使用数字后缀。默认情况下,后缀将为aa
, ab
etc. 而不是01
, 02
etc。
如果有太多文件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 */
}
}