unix - büyük bir .gz dosyasını satır satır bölün


16

Eminim birisi aşağıdaki ihtiyacı olmuştur, büyük bir .gz dosyasını satır satır bölmenin hızlı bir yolu nedir? Temel metin dosyasında 120 milyon satır var. Birisi bir dosya bash / perl komut dosyası veya 3x 40mn satır dosyaları içine bölünmüş olabilir aracı. . yani şöyle çağırıyor:

    bash splitter.sh hugefile.txt.gz 4000000 1
 would get lines 1 to 40 mn    
    bash splitter.sh hugefile.txt.gz 4000000 2
would get lines 40mn to 80 mn
    bash splitter.sh hugefile.txt.gz 4000000 3
would get lines 80mn to 120 mn

Belki de bir dizi çözüm yapıyor mu veya gunzip -c tüm dosyanın sıkıştırılması için yeterli alan gerektirir mi (orijinal sorun): gunzip -c hugefile.txt.gz | kafa 4000000

Not: Fazla disk alamıyorum.

Teşekkürler!


1
Ortaya çıkan dosyaların tekrar gziplenmesini ister misiniz?

Gunzip'i ipe'de kullanabilirsiniz. Gerisi baş ve kuyruk ile yapılabilir
Ingo

@Tichodroma - hayır tekrar gziped onlara ihtiyacım yok. Ancak bölünmüş metin dosyalarını aynı anda depolayamadım. Bu yüzden ilk bölümü almak, onunla bir şeyler yapmak, ardından ilk bölümü silmek ve sonra 2. bölümü almak istiyorum. Sonunda nihayet orijinal gz'yi kaldırmak
toop

1
@toop: Açıklama için teşekkürler. Sorunuzu bir açıklamaya koymak yerine açıklığa kavuşturmak istiyorsanız, genellikle sorunuzu düzenlemenin daha iyi olacağını unutmayın; bu şekilde herkes görecek.
sleske

Parçaların sadece bir kısmını istiyorsanız ve bunları önceden bilmiyorsanız, kabul edilen cevap iyidir. Tüm parçaları bir seferde oluşturmak istiyorsanız, bölünmeye dayalı çözümler çok daha hızlı olacaktır, O (N²) yerine O (N).
b0fh

Yanıtlar:


11

Bunu en iyi nasıl yapmak istediğinize bağlıdır:

  • Büyük dosyanın tek bir bölümünü çıkarmak ister misiniz?
  • Yoksa tüm parçaları tek seferde mi oluşturmak istiyorsunuz?

Eğer bir istiyorsanız dosyanın tek bir bölümünü , senin fikrin kullanmak gunzipve headdoğru. Kullanabilirsiniz:

gunzip -c hugefile.txt.gz | head -n 4000000

Bu, ilk 4000000 satırını standart çıkışta çıkarır - muhtemelen verilerle bir şeyler yapmak için başka bir boru eklemek istersiniz.

Diğer bölümlerini almak için, bir arada kullanmayı tercih headve tailbenzeri:

gunzip -c hugefile.txt.gz | head -n 8000000 |tail -n 4000000

ikinci blok almak için.

Belki de bunlardan bir dizi çözüm yapıyor mu veya gunzip -c, tüm dosyanın sıkıştırılması için yeterli alan gerektiriyor mu?

Hayır, gunzip -cherhangi bir disk alanı gerektirmez - bellekteki her şeyi yapar, sonra stdout'a gönderir.


Tüm parçaları bir seferde oluşturmak istiyorsanız , hepsini tek bir komutla oluşturmak daha verimlidir, çünkü daha sonra giriş dosyası sadece bir kez okunur. İyi bir çözüm kullanmaktır split; ayrıntılar için jim mcnamara'nın cevabına bakınız.


1
Performans görünümünden: gzip aslında tüm dosyayı açıyor mu? Yoksa sadece sihirli bir şekilde sadece 4 milyon satır gerektiğini biliyor mudur?
Alois Mahdal

3
@AloisMahdal: Aslında bu iyi bir ayrı soru olurdu :-). Kısa versiyon: gzipSınırı bilmiyor (farklı bir süreçten geliyor). Eğer headkullanılırsa, headyeterli aldığında çıkar ve bu da iletilecektir gzip(Vikipedi bakın, SIGPIPE yoluyla). İçin tailbu mümkün değildir, bu yüzden evet, gzipher şeyi sıkıştırmasını.Python.
sleske

Eğer ilgileniyorsanız, bunu gerçekten ayrı bir soru olarak sormalısınız.
sleske

20

bölmek için boru dosyayı açmak için gunzip -c veya zcat kullanın

gunzip -c bigfile.gz | split -l 400000

Split komutuna çıktı özellikleri ekleyin.


3
Bu, bölünmüş parçaların sadece bir kısmına ihtiyaç duymadığınız sürece, kabul edilen cevaptan çok daha etkilidir. Lütfen oy verin.
b0fh

1
@ b0fh: Evet, haklısın. Oy verildi ve cevabımda referans verildi :-).
sleske

Kesinlikle en iyi cevap.
Stephen Blum

çıktılar .gz dosyaları kendilerini böylece çıkış özellikleri nelerdir?
Quetzalcoatl

7

(Geri alınamaz) bir akış üzerinde çalışırken, N satırından başlayarak satırları almak için '+ N' kuyruk formunu kullanmak isteyeceksiniz.

zcat hugefile.txt.gz | head -n 40000000
zcat hugefile.txt.gz | tail -n +40000001 | head -n 40000000
zcat hugefile.txt.gz | tail -n +80000001 | head -n 40000000


3

.Gz dosyasını doğrudan .gz dosyalarına bölün:

zcat bigfile.gz | split -l 400000 --filter='gzip > $FILE.gz'

Bence OP'nin istediği bu, çünkü fazla yeri yok.


2

İşte bir dizinden globbed bir dosya seti açmak, gerekirse onları silahla açmak ve satır satır okumak için bir python betiği. Sadece dosya adlarını ve geçerli satırı ve biraz ek yükü tutmak için bellekte gerekli alanı kullanır.

#!/usr/bin/env python
import gzip, bz2
import os
import fnmatch

def gen_find(filepat,top):
    for path, dirlist, filelist in os.walk(top):
        for name in fnmatch.filter(filelist,filepat):
            yield os.path.join(path,name)

def gen_open(filenames):
    for name in filenames:
        if name.endswith(".gz"):
            yield gzip.open(name)
        elif name.endswith(".bz2"):
            yield bz2.BZ2File(name)
        else:
            yield open(name)

def gen_cat(sources):
    for s in sources:
        for item in s:
            yield item

def main(regex, searchDir):
    fileNames = gen_find(regex,searchDir)
    fileHandles = gen_open(fileNames)
    fileLines = gen_cat(fileHandles)
    for line in fileLines:
        print line

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Search globbed files line by line', version='%(prog)s 1.0')
    parser.add_argument('regex', type=str, default='*', help='Regular expression')
    parser.add_argument('searchDir', , type=str, default='.', help='list of input files')
    args = parser.parse_args()
    main(args.regex, args.searchDir)

Baskı satırı komutu her satırı std çıkışına gönderir, böylece bir dosyaya yeniden yönlendirebilirsiniz. Alternatif olarak, satırlarla ne yapmak istediğinizi bize bildirirseniz, bunu python komut dosyasına ekleyebilirim ve dosyanın parçalarını bırakmaya gerek kalmaz.


2

Burada, stdin'i okumak ve satırları bölmek için kullanılabilen bir perl programı vardır, her kümeyi farklı bir hedefe yönlendirmek için $ SPLIT kabuk değişkeni kullanabilen ayrı bir komuta borulama. Sizin durumunuz için,

zcat hugefile.txt.gz | perl xsplit.pl 40000000 'cat > tmp$SPLIT.txt; do_something tmp$SPLIT.txt; rm tmp$SPLIT.txt'

Üzgünüm, komut satırı işleme biraz kludgy ama fikir anladınız.

#!/usr/bin/perl -w
#####
# xsplit.pl: like xargs but instead of clumping input into each command's args, clumps it into each command's input.
# Usage: perl xsplit.pl LINES 'COMMAND'
# where: 'COMMAND' can include shell variable expansions and can use $SPLIT, e.g.
#   'cat > tmp$SPLIT.txt'
# or:
#   'gzip > tmp$SPLIT.gz'
#####
use strict;

sub pipeHandler {
    my $sig = shift @_;
    print " Caught SIGPIPE: $sig\n";
    exit(1);
}
$SIG{PIPE} = \&pipeHandler;

my $LINES = shift;
die "LINES must be a positive number\n" if ($LINES <= 0);
my $COMMAND = shift || die "second argument should be COMMAND\n";

my $line_number = 0;

while (<STDIN>) {
    if ($line_number%$LINES == 0) {
        close OUTFILE;
        my $split = $ENV{SPLIT} = sprintf("%05d", $line_number/$LINES+1);
        print "$split\n";
        my $command = $COMMAND;
        open (OUTFILE, "| $command") or die "failed to write to command '$command'\n";
    }
    print OUTFILE $_;
    $line_number++;
}

exit 0;
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.