多数のファイルをマージ

多数のファイルをマージ

res.1±10,000個のファイル( - )があり、res.10000すべて1つの列と同じ数の行で構成されています。私が望むのは本質的に簡単です。すべてのファイルを列単位で新しいファイルにマージしますfinal.res。私は以下を試してみました。

paste res.*

しかし、(これは結果ファイルの小さなサブセットに対して機能するようですが、コレクション全体で実行すると、次のエラーが発生しますToo many open files

これを達成するための「簡単な」方法が必要です。しかし残念ながら、私はUnixに初めて接する人です。よろしくお願いします!

PS:(私)データファイルの1つがどのように見えるかについてのアイデアを提供するには:

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

答え1

マシンへのルートアクセス権がある場合は、開いているファイルディスクリプタの最大数の制限を一時的に増やすことができます。

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

それから

paste res.* >final.res

後で元の値にリセットできます。


2番目の解決策、制限を変更できない場合:

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

ファイルごとに1回呼び出され、pasteすべての列を含む巨大なファイルが生成されます(1分かかります)。

編集する:猫に役に立たない用途...いいえ

コメントで述べたように、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

今回は、etcというファイルに含まれている1000個のファイルが一覧表示され、そのファイルがlists00etclists01というres.ファイルに貼り付けられ、最後merge00に部分的merge01にマージされた結果ファイルがすべてマージされます。

言ったように錯乱一度に使用するファイルの数を増やすことができます。制限は、指定された値からulimit -n開いているファイルの数を引いたものです。

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

使用制限から10ポイント割引されます。

あなたのバージョンがsplitそれをサポートしていない場合は-d削除できます。split数字のサフィックスを使用するように求められます。デフォルトではaa、サフィックスは背の代わりに背にabなります0102

ファイルが多すぎて失敗する場合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、貼り付け、*など)を超えると思います。

このために、10,000個のファイルや数十万行(それぞれ10行の10,000個のファイル(例では行の最大サイズ))を開かない小さなプログラムを作成します。各ファイルから読み取られたバイト数を格納するには、約10,000個の整数配列のみが必要です。欠点は、ファイル記述子が1つだけあり、ファイルごとに1行あたりに再利用されるため、速度が遅くなる可能性があることです。

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 */
   }
}

関連情報