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 split
diese 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.txt
und 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 csplit
Befehl 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 sed
zweiten 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:sed
sed
sed
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