von Zeile zu Zeile extrahieren und dann in einer separaten Datei speichern

7

Ich habe mein Glück versucht grepund sedirgendwie schaffe ich es nicht, es richtig zu machen.

Ich habe eine Protokolldatei mit einer Größe von ca. 8 GB. Ich muss einen Zeitraum von 15 Minuten verdächtiger Aktivitäten analysieren. Ich habe den Teil der Protokolldatei gefunden, den ich betrachten muss, und ich versuche, diese Zeilen zu extrahieren und in einer separaten Datei zu speichern. Wie würde ich das auf einem normalen CentOS-Computer machen?

Mein letzter Versuch war dies, aber es hat nicht funktioniert. Ich bin ratlos, wenn es um seddiese Art von Befehlen geht.

sed -n '2762818,2853648w /var/log/output.txt' /var/log/logfile
koljanep
quelle

Antworten:

9
sed -n '2762818,2853648p' /var/log/logfile > /var/log/output.txt

p ist für den Druck

thiagowfx
quelle
Vielen Dank. Gibt es eine Möglichkeit sed, eine neue Datei zu erstellen, wenn keine vorhanden ist? Im Moment bekomme ich sed: 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.
Koljanep
Dieser Befehl erstellt die Datei /var/log/logfileautomatisch, wenn sie nicht vorhanden ist. Es wird es sogar ersetzen, wenn es bereits existiert. Der Punkt ist: /var/log/logfileist die Datei, die die gewünschten Zeilen enthält. Die Datei, aus der Sie lesen möchten, lautet /var/log/output.txt: Ich habe gerade Ihr Beispiel kopiert. Anscheinend versuchen Sie, aus einer nicht vorhandenen Datei zu lesen . Sie sollten es durch den tatsächlichen Pfad ersetzen, in dem sich die zu lesende Protokolldatei befindet.
Thiagowfx
1
Ops, du hast recht. Ich habe mir nicht die Mühe gemacht, den Namen der Dateien zu lesen. Ich habe nur angenommen, dass der linke eingegeben und der rechte ausgegeben wurde. Ich werde meine Antwort aktualisieren.
Thiagowfx
2

Der wahrscheinlich beste Weg, dies zu tun, ist die Shell-Umleitung, wie andere erwähnt haben. sedObwohl 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 sedhabe 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 ... ; } <inputalle Eingaben dieselbe Eingabe haben. Die meisten Befehle erschöpfen die gesamte Datei, während sie gelesen werden. In einem { cmd1 ; cmd2; } <infileFall wird normalerweise cmd1vom Kopf der Datei bis zu ihrem Ende gelesen und cmd2es verbleibt keine.

headwird 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/nullund 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 $nund zunächst $dauf ... gesetzt.

  • $n
    • Die Zeilenanzahl, wie von wcfür meine Testdatei angegeben/tmp/5mil_lines
  • $d
    • Der Quotient von $n/43wobei 43 nur ein willkürlich ausgewählter Divisor ist.

Es werden dann Schleifen, um die untiles auf einen Wert weniger dekrementiert $nwurde $d, wiederholt $d. Dabei speichert es seinen Split-Count in $sund verwendet diesen Wert in der Schleife, um die benannte >Ausgabedatei aufzurufen /tmp/[num].split. Das Ergebnis ist, dass \nfü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 wces 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 
mikeserv
quelle
@don_crissti - warte, was? tacmüsste die ganze Datei essen - genau wie tailich denke - aber ich würde denken, wenn Sie das headDing zuerst machen, sollten Sie in der Lage sein, nur den letzten Teil der Datei umzukehren. Passiert das nicht? Entschuldigung, das hat mich nur überrascht. Aber wenn man es immer mehr betrachtet, ist es eine interessante Idee.
Mikeserv
@don_crissti - immer interessanter ... Ich werde es versuchen strace. Oh, warte eine Minute - tacmuss stdin testen, um nach einer suchbaren Eingabe zu suchen und den Deskriptor zurückzuspulen - es ist das einzige, was für mich Sinn macht. Ich werde es aber überprüfen strace. Das wäre übrigens schlechtes Benehmen, denke ich.
Mikeserv
@don_crissti - Ja - es tut lseek(): [pid 6542] lseek(0, 0, SEEK_END) = 551 [pid 6542] ioctl(0, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7fff51f3a940) = -1 ENOTTY (Inappropriate ioctl for device) [pid 6542] lseek(0, 0, SEEK_END) = 551 [pid 6542] lseek(0, 0, SEEK_SET) = 0- an sich kein schlechtes Verhalten, wenn man bedenkt, was taczu tun ist, aber abnormal und im Gegensatz zu dem, was die Standarddienstprogramme tun sollten.
Mikeserv
@don_crissti - überhaupt nicht.
Mikeserv
Mike, das hat nichts mit deiner Antwort hier zu tun, aber ich bin nur neugierig ... Wenn du die Frage noch einmal liest, warum würde OPs "letzter Versuch" nicht funktionieren? Gibt es sedda draußen, die nicht unterstützen w? Die akzeptierte Antwort macht so ziemlich dasselbe nur mit pund >... macht keinen Sinn ...
don_crissti
0

Sie könnten wahrscheinlich dies mit Hilfe erreichen headund tailBefehlskombinationen wie unten.

head -n{to_line_number} logfile | tail -n+{from_line_number} > newfile

Ersetzen Sie das from_line_numberund to_line_numberdurch 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
Ramesh
quelle