
Ich habe mein Glück mit grep
und versucht, sed
aber irgendwie bekomme ich es nicht hin.
Ich habe eine Protokolldatei mit einer Größe von etwa 8 GB. Ich muss einen 15-minütigen Zeitraum verdächtiger Aktivitäten analysieren. Ich habe den Teil der Protokolldatei gefunden, den ich mir ansehen muss, und versuche, diese Zeilen zu extrahieren und in einer separaten Datei zu speichern. Wie würde ich das auf einer normalen CentOS-Maschine machen?
Mein letzter Versuch war dieser, aber er hat nicht funktioniert. Ich bin ratlos, wenn es um sed
diese Art von Befehlen geht.
sed -n '2762818,2853648w /var/log/output.txt' /var/log/logfile
Antwort1
sed -n '2762818,2853648p' /var/log/logfile > /var/log/output.txt
p
ist für den Druck
Antwort2
Der beste Weg hierfür ist wahrscheinlich, wie andere bereits erwähnt haben, die Shell-Umleitung. Dies ist sed
zwar mein persönlicher Favorit, wird dies aber vermutlich nicht effizienter erledigen als will head
, das darauf ausgelegt ist, nur eine bestimmte Anzahl von Zeilen aus einer Datei zu erfassen.
Auf dieser Site gibt es weitere Antworten, die nachweislich zeigen, dass bei großen Dateien die Leistung immer head -n[num] | tail -n[num]
besser ist , aber wahrscheinlich noch schneller ist es, ganz auf die Pipe zu verzichten.sed
Ich habe eine Datei wie diese erstellt:
echo | dd cbs=5000000 conv=block | tr \ \\n >/tmp/5mil_lines
Und ich habe es durchlaufen lassen:
{ head -n "$((ignore=2762817))" >&2
head -n "$((2853648-ignore))"
} </tmp/5mil_lines 2>/dev/null |
sed -n '1p;$p'
Ich habe sed
dort nur „überhaupt“ verwendet, um nur die erste und die letzte Zeile zu erfassen und Ihnen zu zeigen …
2762818
2853648
Dies funktioniert, weil Sie Befehle gruppieren { ... ; }
und die Eingabe für die Gruppe umleiten, als ... ; } <input
würden alle dieselbe Eingabe verwenden. Die meisten Befehle verbrauchen beim Lesen die gesamte Eingabedatei, sodass in einem { cmd1 ; cmd2; } <infile
Fall normalerweise cmd1
vom Anfang bis zum Ende der Eingabedatei gelesen wird und cmd2
nichts übrig bleibt.
head
wird jedoch immer nur so weit in seinem Infile suchen, wie es angewiesen wurde, und so in einem …
{ head -n [num] >/dev/null
head -n [num]
} <infile
… Fall sucht der erste bis [num]
und gibt seine Ausgabe dorthin aus, /dev/null
und der zweite kann dort mit dem Lesen beginnen, wo der erste aufgehört hat.
Du kannst tun...
{ head -n "$((ignore=2762817))" >/dev/null
head -n "$((2853648-ignore))" >/path/to/outfile
} <infile
Diese Konstruktion funktioniert auch mit anderen zusammengesetzten Befehlen. Beispiel:
set "$((n=2762817))" "$((2853648-n))"
for n do head "-n$n" >&"$#"; shift
done <5mil_lines 2>/dev/null |
sed -n '1p;$p'
...der 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
Oben setzt die Shell zunächst die $n
und $d
Variablen auf ...
$n
wc
Die von mir für meine Testdatei gemeldete Zeilenanzahl/tmp/5mil_lines
$d
- Der Quotient von
$n/43
43 ist lediglich ein beliebig gewählter Teiler.
- Der Quotient von
Anschließend führt es eine Schleife aus, die es um auf einen Wert kleiner until
dekrementiert hat . Dabei speichert es seinen Split-Count in und verwendet diesen Wert in der Schleife, um die benannte Ausgabedatei mit dem Namen zu erhöhen . Das Ergebnis ist, dass es für jede Iteration eine gleiche Anzahl von durch Zeilen getrennten Feldern in seiner Eingabedatei in eine neue Ausgabedatei liest – und sie im Verlauf der Schleife 43 Mal gleichmäßig aufteilt. Es schafft dies, ohne seine Eingabedatei mehr als 2 Mal lesen zu müssen – das erste Mal, wenn es seine Zeilen zählt, und für den Rest der Operation liest es nur so viele Zeilen, wie es jedes Mal in die Ausgabedatei schreibt.$n
$d
$d
$s
>
/tmp/[num].split
\n
wc
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
Antwort3
Dies können Sie wahrscheinlich mithilfe der folgenden Befehlskombinationen head
erreichen .tail
head -n{to_line_number} logfile | tail -n+{from_line_number} > newfile
Ersetzen Sie das from_line_number
und to_line_number
durch die gewünschten Zeilennummern.
Testen
cat logfile
This is first line.
second
Third
fourth
fifth
sixth
seventh
eighth
ninth
tenth
##I use the command as below. I extract from 4th line to 10th line.
head -n10 logfile | tail -n+4 > newfile
fourth
fifth
sixth
seventh
eighth
ninth
tenth