Çok sayıda dosyayı birleştirme


15

± 10.000 dosyam ( res.1- res.10000) hepsi bir sütun ve eşit sayıda satırdan oluşuyor. İstediğim, özünde basit; tüm dosyaları yeni bir dosyada sütun olarak birleştirin final.res. Kullanmayı denedim:

paste res.*

Bu sonuç, dosyaların bir kısmı için daha çalışmak görünse de bütün sette gerçekleştirilen Ancak (bu aşağıdaki hata veriyor: Too many open files.

Bunu yapmanın 'kolay' bir yolu olmalı, ama maalesef unix için oldukça yeniyim. Şimdiden teşekkürler!

Not: Veri dosya (larımdan) birinin nasıl göründüğü hakkında bir fikir vermek için:

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

Bu --serialseçeneği pastekomutla kullanmayı denediniz mi?
shivams

@shivams paste --serialdosyaları sütun bazında birleştirmez ...
Stephen Kitt

@StephenKitt Bekle. Biraz kafam karıştı. Çıktı dosyasında her bir dosyanın verileri için farklı bir sütuna ihtiyacı olduğu anlamına mı geliyor? Yoksa tüm veriler tek bir sütunda mı?
shivamlar

@Stephen Kitt shivams paste -sGerçekten işe yarıyor, ancak ayrı sonuç dosyalarını sütun-bilge yerine satır- bilye yapıştırıyor. Ancak bu çözebileceğim bir şey. Teşekkürler!
paspaslar

Ben çıktı dosyasındaki her dosyanın verilerini farklı sütunun @shivams
papasları

Yanıtlar:


17

Bu makinede kök izinleriniz varsa, "maksimum açık dosya tanımlayıcı sayısı" sınırını geçici olarak artırabilirsiniz:

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

Ve sonra

paste res.* >final.res

Bundan sonra orijinal değerlerine geri ayarlayabilirsiniz.


Bir ikinci çözelti , sen sınırı değiştiremiyorsanız:

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

pasteHer dosyayı bir kez çağırır ve sonunda tüm sütunları içeren büyük bir dosya vardır (dakika alır).

Edit : Kedi yararsız kullanımı ... Hayır !

Yorumlarda belirtildiği gibi, catburada ( cat final.res | paste - $f >temp) kullanımı işe yaramaz. Döngü ilk kez çalıştırıldığında, dosya final.reszaten mevcut değildir. pastesonra başarısız olur ve dosya asla doldurulmaz veya oluşturulur. Benim çözüm ile sadece catilk kez başarısız olurNo such file or directory ve pastestdin sadece boş bir dosya okur, ama devam ediyor. Hata göz ardı edilebilir.


Teşekkürler! Orijinal değerlerin ne olduğunu nasıl kontrol edebilirim?
paspaslar

Sadece ulimit -Snyumuşak limit ve ulimit -Hnsert limit için
kaos

Teşekkürler, bu kısmen işe yarıyor. Ancak, dosya başka bir dizi için aşağıdaki hatayı alıyorum: -bash: /usr/bin/paste: Argument list too long. Bunu nasıl çözeceğiniz hakkında fikirler? Sizi rahatsız ettiğim için özür dilerim.
paspaslar

@mats, çekirdeğinizin daha fazla argümana izin vermediğini, bunu kontrol getconf ARG_MAXedebileceğinizi, yalnızca çekirdeği yeniden derlerken bu değeri artırabileceğinizi gösteriyor. İkinci çözümümü deneyebilir misin?
kaos

2
catDöngü boyunca her seferinde kullanmak yerine, boş bir final.resdosya oluşturarak başlayabilirsiniz . Zaten final.resorada bir dosya olması durumunda, bu muhtemelen iyi bir fikirdir .
Barmar

10

Eğer CHAOS (eğer gerekli izinlere sahip olmadığınızdan) 'cevabı uygulanamaz, toplu kadar olabilir pastearamalar şöyle:

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

Bu lists00, lists01vb. Adlı dosyalarda bir kerede 1000 dosyayı listeler , ardından ilgili dosyaları yapıştırır.res. dosyaları merge00, merge01vb. Adlı dosyalara ve son olarak, sonuçta kısmen birleştirilen tüm dosyaları birleştirir.

Kaosun belirttiği gibi , aynı anda kullanılan dosya sayısını artırabilirsiniz; sınır ulimit -neksi verilen değerdir, ancak zaten açık olan birçok dosyadır,

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

eksi on sınırını kullanmak için.

Sürümünüz splitdesteklenmiyorsa -dkaldırabilirsiniz: tek yapmanız splitgereken sayısal sonekleri kullanmaktır. Varsayılan olarak ekleri olacak aa, abvb yerine 01,02 vb

ls -1 res.*Başarısız olan çok fazla dosya varsa ("bağımsız değişken listesi çok uzun"), findbu hatayı değiştirerek bu hatayı önleyebilirsiniz:

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

( Don_crissti tarafından işaret edildiği gibi , -1boru lsçıkışında gerekli olmamalıdır ; ancak ls, takma adı olan durumları işlemek için bırakıyorum -C.)


4

Bu şekilde yürütmeyi deneyin:

ls res.*|xargs paste >final.res

Toplu işi parçalara bölebilir ve şöyle bir şey deneyebilirsiniz:

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

ve sonunda son dosyaları birleştirin

paste final.* >final.res

@ Romeo Ninov Bu benim ilk sorum metioned aynı hata veriyor:Too many open files
paspaslar

@mats, bu durumda partiyi parçalara ayırmayı düşündünüz. Size fikir vermek için cevabımı düzenleyeceğim
Romeo Ninov

Doğru, @StephenKitt, cevabımı
düzenliyorum

Geçici dosyalardan kaçınmak için, final.x00be borularını - ya FIFO'lar olarak adlandırılmış ya da örtük olarak, işlem yerine koymayı (kabuğunuz destekliyorsa - örneğin bash) yapmayı düşünün . El ile yazmak eğlenceli değil, ancak bir makefile için uygun olabilir.
Toby Speight

4
i=0
{ paste res.? res.?? res.???
while paste ./res."$((i+=1))"[0-9][0-9][0-9]
do :; done; } >outfile

Bunun kadar karmaşık olduğunu düşünmüyorum - dosya adlarını sipariş ederek çok çalıştınız. Sadece hepsini aynı anda açmayın, hepsi bu.

Diğer yol:

pst()      if   shift "$1"
           then paste "$@"
           fi
set ./res.*
while  [ -n "${1024}" ] ||
     ! paste "$@"
do     pst "$(($#-1023))" "$@"
       shift 1024
done >outfile

... ama bence bu onları geriye doğru yapıyor ... Bu daha iyi olabilir:

i=0;  echo 'while paste \'
until [ "$((i+=1))" -gt 1023 ] &&
      printf '%s\n' '"${1024}"' \
      do\ shift\ 1024 done
do    echo '"${'"$i"'-/dev/null}" \'
done | sh -s -- ./res.* >outfile

Ve işte yine başka bir yol:

tar --no-recursion -c ./ |
{ printf \\0; tr -s \\0; }    |
cut -d '' -f-2,13              |
tr '\0\n' '\n\t' >outfile

Bu, tartüm dosyaları sizin için boş olarak ayrılmış bir akışta toplamanızı sağlar , dosya adı hariç tüm üstbilgi meta verilerini ayrıştırır ve tüm dosyalardaki tüm satırları sekmelere dönüştürür. Girişin gerçek metin dosyaları olmasına dayanır - yani her biri yeni satırla biter ve dosyalarda boş bayt yoktur. Oh - ve aynı zamanda kendilerini yeni satır-özgür olmak dosya adları dayanır (yani GNU ile sağlam ele olabilir gerçi tar'ın --xformseçeneği) . Bu koşulların karşılandığı göz önüne alındığında, herhangi bir sayıda dosya üzerinde çok kısa bir çalışma yapmalıdır - vetar yapmalı neredeyse hepsini yapacaktır.

Sonuç, şuna benzeyen bir dizi satırdır:

./fname1
C1\tC2\tC3...
./fname2
C1\tC2\t...

Ve bunun gibi.

İlk olarak 5 test dosyası oluşturarak test ettim. Şu anda 10000 dosyaya genning gibi hissetmedim, bu yüzden her biri için biraz daha büyüdüm - ve dosya uzunluklarının büyük ölçüde farklı olmasını sağladım . tarKomut dosyalarını test ederken bu önemlidir, çünkü tarsabit uzunluklarda girişi engelleyecektir - en az birkaç farklı uzunluk denemediğinizde, yalnızca gerçek olanı ele alıp almayacağınızı asla bilemezsiniz.

Her neyse, yaptığım test dosyaları için:

for f in 1 2 3 4 5; do : >./"$f"
seq "${f}000" | tee -a [12345] >>"$f"
done

ls daha sonra rapor edildi:

ls -sh [12345]
68K 1 68K 2 56K 3 44K 4 24K 5

... sonra koştum ...

tar --no-recursion -c ./ |
{ printf \\0; tr -s \\0; }|
cut -d '' -f-2,13          |
tr '\0\n' '\n\t' | cut -f-25

... sadece satır başına yalnızca 25 sekmeyle ayrılmış alanı göstermek için (her dosya tek bir satır olduğu için çok fazla var ) ...

Çıktı şuydu:

./1
1    2    3    4    5    6    7    8    9    10    11    12    13    14    15    16    17    18    19    20    21    22    23    24    25
./2
1    2    3    4    5    6    7    8    9    10    11    12    13    14    15    16    17    18    19    20    21    22    23    24    25
./3
1    2    3    4    5    6    7    8    9    10    11    12    13    14    15    16    17    18    19    20    21    22    23    24    25
./4
1    2    3    4    5    6    7    8    9    10    11    12    13    14    15    16    17    18    19    20    21    22    23    24    25
./5
1    2    3    4    5    6    7    8    9    10    11    12    13    14    15    16    17    18    19    20    21    22    23    24    25

4

İlgili dosya, çizgi boyutu vb. Miktarı göz önüne alındığında, varsayılan araç boyutlarını (awk, sed, yapıştır, *, vb.) Aşacağını düşünüyorum.

Bunun için küçük bir program yaratacağım, ne 10.000 dosya açık ne de yüz binlerce uzunluğunda bir satır (10.000 dosya 10 (örnekte maksimum satır boyutu)) olurdu. Her dosyadan okunan bayt sayısını saklamak için yalnızca ~ 10.000 tamsayı dizisi gerekir. Dezavantajı, yalnızca bir dosya tanımlayıcıya sahip olması, her dosya için, her satır için yeniden kullanılması ve bu yavaş olabilir.

Tanımları FILESve ROWSfiili kesin değerlere değiştirilmelidir. Çıktı standart çıktıya gönderilir.

#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 */
   }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.