Der wahrscheinlich beste Weg, dies zu tun, ist die Shell-Umleitung, wie andere erwähnt haben. sed
Obwohl dies ein persönlicher Favorit ist, wird dies wahrscheinlich nicht effizienter als der Wille sein head
- was darauf ausgelegt ist, nur so viele Zeilen aus einer Datei zu erfassen.
Es gibt andere Antworten auf dieser Site, die nachweislich zeigen, dass große Dateien jedes Mal head -n[num] | tail -n[num]
eine Outperformance erzielen sed
, aber wahrscheinlich sogar noch schneller, als die Pipe insgesamt zu meiden.
Ich habe eine Datei erstellt wie:
echo | dd cbs=5000000 conv=block | tr \ \\n >/tmp/5mil_lines
Und ich habe es durchlaufen:
{ head -n "$((ignore=2762817))" >&2
head -n "$((2853648-ignore))"
} </tmp/5mil_lines 2>/dev/null |
sed -n '1p;$p'
Ich sed
habe dort überhaupt nur die erste und letzte Zeile genommen, um Ihnen zu zeigen ...
2762818
2853648
Dies funktioniert, da beim Gruppieren von Befehlen mit { ... ; }
und Umleiten der Eingabe für die Gruppe ... ; } <input
alle Eingaben dieselbe Eingabe haben. Die meisten Befehle erschöpfen die gesamte Datei, während sie gelesen werden. In einem { cmd1 ; cmd2; } <infile
Fall wird normalerweise cmd1
vom Kopf der Datei bis zu ihrem Ende gelesen und cmd2
es verbleibt keine.
head
wird jedoch immer nur so weit durch seine Infile suchen, wie es angewiesen ist, und so in einem ...
{ head -n [num] >/dev/null
head -n [num]
} <infile
... Fall, dass der erste [num]
seine Ausgabe durchsucht und ausgibt /dev/null
und der zweite übrig bleibt, um mit dem Lesen dort zu beginnen, wo der erste ihn verlassen hat.
Du kannst tun...
{ head -n "$((ignore=2762817))" >/dev/null
head -n "$((2853648-ignore))" >/path/to/outfile
} <infile
Dieses Konstrukt funktioniert auch mit anderen Arten von zusammengesetzten Befehlen. Zum Beispiel:
set "$((n=2762817))" "$((2853648-n))"
for n do head "-n$n" >&"$#"; shift
done <5mil_lines 2>/dev/null |
sed -n '1p;$p'
... was druckt ...
2762818
2853648
Es könnte aber auch so funktionieren:
d=$((( n=$(wc -l </tmp/5mil_lines))/43 )) &&
until [ "$(((n-=d)>=(!(s=143-n/d))))" -eq 0 ] &&
head "-n$d" >>"/tmp/${s#1}.split"
do head "-n$d" > "/tmp/${s#1}.split" || ! break
done </tmp/5mil_lines
Über der Shell werden die Variablen $n
und zunächst $d
auf ... gesetzt.
$n
- Die Zeilenanzahl, wie von
wc
für meine Testdatei angegeben/tmp/5mil_lines
$d
- Der Quotient von
$n/43
wobei 43 nur ein willkürlich ausgewählter Divisor ist.
Es werden dann Schleifen, um die until
es auf einen Wert weniger dekrementiert $n
wurde $d
, wiederholt $d
. Dabei speichert es seinen Split-Count in $s
und verwendet diesen Wert in der Schleife, um die benannte >
Ausgabedatei aufzurufen /tmp/[num].split
. Das Ergebnis ist, dass \n
für jede Iteration eine gleiche Anzahl von durch E-Zeilen getrennten Feldern in der Infile in eine neue Outfile ausgelesen wird, die im Verlauf der Schleife 43-mal gleichmäßig aufgeteilt wird. Es verwaltet es, ohne seine Infile mehr als zweimal lesen zu müssen - das erste Mal ist, wann wc
es seine Zeilen zählt, und für den Rest des Vorgangs liest es jedes Mal nur so viele Zeilen, wie es in die Outfile schreibt.
Nachdem ich es ausgeführt hatte, überprüfte ich meine Ergebnisse wie ...
tail -n1 /tmp/*split | grep .
AUSGABE:
==> /tmp/01.split <==
116279
==> /tmp/02.split <==
232558
==> /tmp/03.split <==
348837
==> /tmp/04.split <==
465116
==> /tmp/05.split <==
581395
==> /tmp/06.split <==
697674
==> /tmp/07.split <==
813953
==> /tmp/08.split <==
930232
==> /tmp/09.split <==
1046511
==> /tmp/10.split <==
1162790
==> /tmp/11.split <==
1279069
==> /tmp/12.split <==
1395348
==> /tmp/13.split <==
1511627
==> /tmp/14.split <==
1627906
==> /tmp/15.split <==
1744185
==> /tmp/16.split <==
1860464
==> /tmp/17.split <==
1976743
==> /tmp/18.split <==
2093022
==> /tmp/19.split <==
2209301
==> /tmp/20.split <==
2325580
==> /tmp/21.split <==
2441859
==> /tmp/22.split <==
2558138
==> /tmp/23.split <==
2674417
==> /tmp/24.split <==
2790696
==> /tmp/25.split <==
2906975
==> /tmp/26.split <==
3023254
==> /tmp/27.split <==
3139533
==> /tmp/28.split <==
3255812
==> /tmp/29.split <==
3372091
==> /tmp/30.split <==
3488370
==> /tmp/31.split <==
3604649
==> /tmp/32.split <==
3720928
==> /tmp/33.split <==
3837207
==> /tmp/34.split <==
3953486
==> /tmp/35.split <==
4069765
==> /tmp/36.split <==
4186044
==> /tmp/37.split <==
4302323
==> /tmp/38.split <==
4418602
==> /tmp/39.split <==
4534881
==> /tmp/40.split <==
4651160
==> /tmp/41.split <==
4767439
==> /tmp/42.split <==
4883718
==> /tmp/43.split <==
5000000
sed
, eine neue Datei zu erstellen, wenn keine vorhanden ist? Im Moment bekomme ichsed: can't read /var/log/output.txt: No such file or directory
. Ich kann natürlich nur eine Datei erstellen, aber zum Lernen möchte ich wissen, wie es automatisch geht.