Große Dateien in Teile aufteilen, ohne den Eintrag aufzuteilen

Große Dateien in Teile aufteilen, ohne den Eintrag aufzuteilen

Ich habe eine ziemlich große MSG-Datei im UIEE-Format.

$ wc -l big_db.msg
8726593 big_db.msg

Im Wesentlichen besteht die Datei aus Einträgen unterschiedlicher Länge, die ungefähr so ​​aussehen:

UR|1
AA|Condon, Richard
TI|Prizzi's Family
CN|Collectable- Good/Good
MT|FICTION
PU|G.P. Putnam & Sons
DP|1986
ED|First Printing.
BD|Hard Cover
NT|0399132104
KE|MAFIA
KE|FICTION
PR|44.9
XA|4
XB|1
XC|BO
XD|S

UR|10
AA|Gariepy, Henry
TI|Portraits of Perseverance
CN|Good/No Jacket
MT|SOLD
PU|Victor Books
DP|1989
BD|Mass Market Paperback
NT|1989 tpb g 100 meditations from the Book of Job "This book...help you
NT| persevere through the struggles of your life..."
KE|Bible
KE|religion
KE|Job
KE|meditations
PR|28.4
XA|4
XB|5
XC|BO
XD|S

Dies ist ein Beispiel für zwei Einträge, die durch eine Leerzeile getrennt sind. Ich möchte diese große Datei in kleinere Dateien aufteilen, ohne einen Eintrag in zwei Dateien aufzuteilen.

Jeder einzelne Eintrag wird in der Datei durch eine neue Zeile (eine komplett leere Zeile) getrennt. Ich möchte diese 8,7 Millionen Zeilen umfassende Datei in 15 Dateien aufteilen. Ich weiß, dass es Tools wie splitdiese gibt, bin mir aber nicht ganz sicher, wie ich die Datei aufteilen kann, sondern sie nur bei einer neuen Zeile aufteilen lassen kann, damit ein einzelner Eintrag nicht in mehrere Dateien aufgeteilt wird.

Antwort1

Unter Verwendung des Vorschlags von csplit:

Aufteilung basierend auf Zeilennummern

$ csplit file.txt <num lines> "{repetitions}"

Beispiel

Angenommen, ich habe eine Datei mit 1000 Zeilen.

$ seq 1000 > file.txt

$ csplit file.txt 100 "{8}"
288
400
400
400
400
400
400
400
400
405

führt zu Dateien wie diesen:

$ wc -l xx*
  99 xx00
 100 xx01
 100 xx02
 100 xx03
 100 xx04
 100 xx05
 100 xx06
 100 xx07
 100 xx08
 101 xx09
   1 xx10
1001 total

Sie können die statische Einschränkung, die Anzahl der Wiederholungen angeben zu müssen, umgehen, indem Sie die Zahlen im Voraus auf Grundlage der Zeilenanzahl Ihrer jeweiligen Datei berechnen.

$ lines=100
$ echo $lines 
100

$ rep=$(( ($(wc -l file.txt | cut -d" " -f1) / $lines) -2 ))
$ echo $rep
8

$ csplit file.txt 100 "{$rep}"
288
400
400
400
400
400
400
400
400
405

Aufteilen basierend auf Leerzeilen

Wenn Sie andererseits eine Datei einfach in die darin enthaltenen Leerzeilen aufteilen möchten, können Sie diese Version von verwenden split:

$ csplit file2.txt '/^$/' "{*}"

Beispiel

Angenommen, ich habe oben 4 leere Zeilen hinzugefügt file.txtund die Datei erstellt file2.txt. Sie können sehen, dass sie manuell hinzugefügt wurden, und zwar so:

$ grep -A1 -B1 "^$" file2.txt
20

21
--
72

73
--
112

113
--
178

179

Oben sehen Sie, dass ich sie zwischen den entsprechenden Zahlen in meiner Beispieldatei eingefügt habe. Wenn ich jetzt den csplitBefehl ausführe:

$ csplit file2.txt '/^$/' "{*}"
51
157
134
265
3290

Sie können sehen, dass ich jetzt 4 Dateien habe, die basierend auf der leeren Zeile aufgeteilt wurden:

$ grep -A1 -B1 '^$' xx0*
xx01:
xx01-21
--
xx02:
xx02-73
--
xx03:
xx03-113
--
xx04:
xx04-179

Verweise

Antwort2

Wenn Ihnen die Reihenfolge der Datensätze egal ist, können Sie Folgendes tun:

gawk -vRS= '{printf "%s", $0 RT > "file.out." (NR-1)%15}' file.in

Andernfalls müssten Sie zunächst die Anzahl der Datensätze ermitteln, um zu wissen, wie viele in jede Ausgabedatei eingefügt werden sollen:

gawk -vRS= -v "n=$(gawk -vRS= 'END {print NR}' file.in)" '
  {printf "%s", $0 RT > "file.out." int((NR-1)*15/n)}' file.in

Antwort3

Hier ist eine Lösung, die funktionieren könnte:

seq 1 $(((lines=$(wc -l </tmp/file))/16+1)) $lines |
sed 'N;s|\(.*\)\(\n\)\(.*\)|\1d;\1,\3w /tmp/uptoline\3\2\3|;P;$d;D' |
sed -ne :nl -ne '/\n$/!{N;bnl}' -nf - /tmp/file

Es funktioniert, indem der erste das Skript des sedzweiten schreiben kann. Der zweite sammelt zunächst alle Eingabezeilen, bis er auf eine leere Zeile stößt. Dann schreibt er alle Ausgabezeilen in eine Datei. Der erste schreibt ein Skript für den zweiten und weist ihn an, wohin seine Ausgabe geschrieben werden soll. In meinem Testfall sah dieses Skript folgendermaßen aus:sedsedsed

1d;1,377w /tmp/uptoline377
377d;377,753w /tmp/uptoline753
753d;753,1129w /tmp/uptoline1129
1129d;1129,1505w /tmp/uptoline1505
1505d;1505,1881w /tmp/uptoline1881
1881d;1881,2257w /tmp/uptoline2257
2257d;2257,2633w /tmp/uptoline2633
2633d;2633,3009w /tmp/uptoline3009
3009d;3009,3385w /tmp/uptoline3385
3385d;3385,3761w /tmp/uptoline3761
3761d;3761,4137w /tmp/uptoline4137
4137d;4137,4513w /tmp/uptoline4513
4513d;4513,4889w /tmp/uptoline4889
4889d;4889,5265w /tmp/uptoline5265
5265d;5265,5641w /tmp/uptoline5641

Ich habe es folgendermaßen getestet:

printf '%s\nand\nmore\nlines\nhere\n\n' $(seq 1000) >/tmp/file

Dadurch erhielt ich eine Datei mit 6000 Zeilen, die folgendermaßen aussah:

<iteration#>
and
more
lines
here
#blank

...1000 mal wiederholt.

Nach dem Ausführen des obigen Skripts:

set -- /tmp/uptoline*
echo $# total splitfiles
for splitfile do
    echo $splitfile
    wc -l <$splitfile
    tail -n6 $splitfile
done    

AUSGABE

15 total splitfiles
/tmp/uptoline1129
378
188
and
more
lines
here

/tmp/uptoline1505
372
250
and
more
lines
here

/tmp/uptoline1881
378
313
and
more
lines
here

/tmp/uptoline2257
378
376
and
more
lines
here

/tmp/uptoline2633
372
438
and
more
lines
here

/tmp/uptoline3009
378
501
and
more
lines
here

/tmp/uptoline3385
378
564
and
more
lines
here

/tmp/uptoline3761
372
626
and
more
lines
here

/tmp/uptoline377
372
62
and
more
lines
here

/tmp/uptoline4137
378
689
and
more
lines
here

/tmp/uptoline4513
378
752
and
more
lines
here

/tmp/uptoline4889
372
814
and
more
lines
here

/tmp/uptoline5265
378
877
and
more
lines
here

/tmp/uptoline5641
378
940
and
more
lines
here

/tmp/uptoline753
378
125
and
more
lines
here

Antwort4

Versuchenawk

awk 'BEGIN{RS="\n\n"}{print $0 > FILENAME"."FNR}' big_db.msg

verwandte Informationen