2014-07-03 8 views
5

Ich habe 40 Dateien zu je 2GB, die auf einer NFS-Architektur gespeichert sind. Jede Datei enthält zwei Spalten: eine numerische ID und ein Textfeld. Jede Datei ist bereits sortiert und gezippt.Merge sort gzipped files

Wie kann ich alle diese Dateien zusammenführen, so dass die resultierende Ausgabe auch sortiert wird?

Ich weiß sort -m -k 1 sollte den Trick für unkomprimierte Dateien tun, aber ich weiß nicht, wie es mit den komprimiert denjenigen direkt zu tun.

PS: Ich möchte nicht die einfache Lösung des Dekomprimierens der Dateien auf der Festplatte, Zusammenführen und erneutes Komprimieren, da ich nicht genügend Speicherplatz dafür habe.

+2

es ist möglich, ein Programm zu entwickeln, das alle Dateien öffnen, dekomprimiert ein Block auf einmal, merge-sortiere die Daten, outp ut die Ergebnisse (komprimiert, wenn gewünscht), und jede Eingabe nach Bedarf voranbringen - aber der Umfang einer solchen Anstrengung ist hier jenseits einer einfachen Antwort. –

+0

@AndrewMedico Für die Aufzeichnung ist das nicht wahr - 'sort' ist genau solch ein Programm, und' bash' kann es ziemlich leicht mit 'gunzip' kombinieren. Siehe die Antworten für Details. – user4815162342

Antwort

11

Dies ist ein Anwendungsfall für Prozesssubstitution. Angenommen, Sie haben zwei Dateien zu sortieren, sorta.gz und sortb.gz. Sie können die Ausgabe von gunzip -c FILE.gz geben für diese beiden Dateien mit dem <(...) Shell Operator zu sortieren:

sort -m -k1 <(gunzip -c sorta.gz) <(gunzip -c sortb.gz) >sorted 

Prozess Substitution ersetzt einen Befehl mit einem Dateinamen, der die Ausgabe des Befehls darstellt, und wird in der Regel umgesetzt mit entweder eine Named Pipe oder eine /dev/fd/... Spezialdatei.

Für 40 Dateien, sollten Sie den Befehl mit, dass viele Prozess Ersetzungen dynamisch erstellen, und verwenden Sie eval es auszuführen:

cmd="sort -m -k1 " 
for input in file1.gz file2.gz file3.gz ...; do 
    cmd="$cmd <(gunzip -c '$input')" 
done 
eval "$cmd" >sorted  # or eval "$cmd" | gzip -c > sorted.gz 
+0

Super - Danke! – mossaab

-1

Es stimmt, es gibt zgrep und andere gebräuchliche Dienstprogramme, die mit komprimierten Dateien spielen, aber in diesem Fall müssen Sie unkomprimierte Daten sortieren/zusammenführen und das Ergebnis komprimieren.

2
#!/bin/bash 

    FILES=file*.gz    # list of your 40 gzip files 
           # (e.g. file1.gz ... file40.gz) 

    WORK1="merged.gz"   # first temp file and the final file 
    WORK2="tempfile.gz"   # second temp file 

    > "$WORK1"     # create empty final file 
    > "$WORK2"     # create empty temp file 

    gzip -qc "$WORK2" > "$WORK1" # compress content of empty second 
           # file to first temp file 

    for I in $FILES; do 
     echo current file: "$I" 
     sort -k 1 -m <(gunzip -c "$I") <(gunzip -c "$WORK1") | gzip -c > "$WORK2" 
     mv "$WORK2" "$WORK1" 
    done 

Fill $ files den einfachste Weg, mit der Liste der Dateien mit bash Globbing (Datei * gz) oder mit einer Liste von 40 Dateinamen (mit weißen Leerzeichen getrennt). Ihre Dateien in $ FILES bleiben unverändert.

Schließlich sind die 80 GB Daten in $ WORK1 komprimiert. Während der Verarbeitung dieses Skripts wurden keine unkomprimierten Daten auf die Festplatte geschrieben.

1

eine unterschiedlich aromatisierten Multi-File innerhalb einer einzigen Pipeline verschmelzen Hinzufügen - es alle (vorsortierten) Dateien in $OUT/uniques nimmt, sorten verschmilzt sie und komprimiert die Ausgabe wird LZ 4 aufgrund seiner Geschwindigkeit verwendet:

find $OUT/uniques -name '*.lz4' | 
    awk '{print "<(<" $0 " lz4cat)"}' | 
    tr "\n" " " | 
    (echo -n sort -m -k3b -k2 " "; cat -; echo) | 
    bash | 
    lz4 \ 
> $OUT/uniques-merged.tsv.lz4