Wie extrahiere ich einen Textteil durch Regex in Linux Shell? Nehmen wir an, ich habe eine Datei, in der sich in jeder Zeile eine IP-Adresse befindet, jedoch an einer anderen Stelle. Was ist der einfachste Weg, diese IP-Adressen mit üblichen Unix-Befehlszeilenprogrammen zu extrahieren?
Sie könnten grep verwenden, um sie herauszuziehen.
grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' file.txt
Die meisten Beispiele hier stimmen mit 999.999.999.999 überein, was technisch keine gültige IP-Adresse ist.
Die folgenden Angaben beziehen sich nur auf gültige IP-Adressen (einschließlich Netzwerk- und Broadcast-Adressen).
grep -E -o '(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' file.txt
Lassen Sie das -o weg, wenn Sie die gesamte übereinstimmende Zeile sehen möchten.
Normalerweise beginne ich mit grep, um den Regex richtig zu machen.
# [multiple failed attempts here]
grep '[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*' file # good?
grep -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' file # good enough
Dann würde ich versuchen, es in sed
umzuwandeln, um den Rest der Zeile herauszufiltern. (Nach dem Lesen dieses Threads werden Sie und ich das nicht mehr tun: Stattdessen verwenden wir grep -o
.)
sed -ne 's/.*\([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\).*/\1/p # FAIL
In diesem Fall ärgere ich mich sed
normalerweise, weil ich nicht die gleichen Regexen benutzt wie andere. Also gehe ich zu Perl
.
$ Perl -nle '/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/ and print $&'
Perl ist auf jeden Fall gut zu wissen. Wenn Sie ein kleines bisschen CPAN installiert haben, können Sie es sogar kostengünstiger machen:
$ Perl -MRegexp::Common=net -nE '/$RE{net}{IPV4}/ and say $&' file(s)
Dies funktioniert gut für mich in Zugriffsprotokollen.
cat access_log | egrep -o '([0-9]{1,3}\.){3}[0-9]{1,3}'
Brechen wir es Stück für Stück auf.
[0-9]{1,3}
bedeutet ein bis drei Vorkommen des in [] genannten Bereichs. In diesem Fall ist es 0-9. es passt also zu Mustern wie 10 oder 183.
Gefolgt von einem '.'. Wir müssen dem als '.' ist ein Meta-Charakter und hat eine besondere Bedeutung für die Shell.
Wir sind jetzt also bei Mustern wie '123'. '12. ' usw.
Dieses Muster wiederholt sich dreimal (mit '.'). Also schließen wir es in Klammern ein .([0-9]{1,3}\.){3}
Und zum Schluss wiederholt sich das Muster, diesmal jedoch ohne das ".". Deshalb haben wir es im 3. Schritt getrennt aufbewahrt. [0-9]{1,3}
Wenn sich die ips wie in meinem Fall am Anfang jeder Zeile befinden, verwenden Sie:
egrep -o '^([0-9]{1,3}\.){3}[0-9]{1,3}'
dabei ist '^' ein Anker, der am Anfang einer Zeile eine Suche angibt.
Ich habe ein kleines script geschrieben, um meine Protokolldateien besser zu sehen, es ist nichts Besonderes, kann aber vielen Leuten helfen, die Perl lernen. Es führt DNS-Suchvorgänge für die IP-Adressen durch, nachdem diese extrahiert wurden.
Ich schrieb einen informativen Blogartikel zu diesem Thema: Gewusst wie: Extrahieren von IPv4- und IPv6-IP-Adressen aus Nur-Text mit Regex .
In diesem Artikel finden Sie eine detaillierte Anleitung zu den am häufigsten verwendeten unterschiedlichen Mustern für IPs, die häufig extrahiert und mithilfe von regulären Ausdrücken aus normalem Text isoliert werden müssen.
Dieses Handbuch basiert auf CodVerters IP Extractor Quellcode-Tool für die Extraktion und Erkennung von IP-Adressen, wenn dies erforderlich ist .
Wenn Sie die IPv4-Adresse validieren und erfassen möchten, kann dieses Muster die Aufgabe erfüllen:
\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)[.]){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b
oder zur Validierung und Erfassung der IPv4-Adresse mit Prefix ("Schrägstrichnotation"):
\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)[.]){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?/[0-9]{1,2})\b
oder zur Erfassung der Subnetzmaske oder der Platzhaltermaske:
(255|254|252|248|240|224|192|128|0)[.](255|254|252|248|240|224|192|128|0)[.](255|254|252|248|240|224|192|128|0)[.](255|254|252|248|240|224|192|128|0)
oder, um Subnetzmaskenadressen herauszufiltern, machen Sie dies mit Regex negativem Lookahead :
\b((?!(255|254|252|248|240|224|192|128|0)[.](255|254|252|248|240|224|192|128|0)[.](255|254|252|248|240|224|192|128|0)[.](255|254|252|248|240|224|192|128|0)))(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)[.]){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b
Für die Validierung von IPv6 können Sie den Artikel-Link aufrufen, den ich oben in dieser Antwort hinzugefügt habe.
Hier ist ein Beispiel für die Erfassung aller gängigen Muster (aus dem CodVerter-Hilfethema IP Extractor):
Wenn Sie möchten, können Sie den IPv4-Regex hier testen.
grep -E -o "([0-9] {1,3} [.]) {3} [0-9] {1,3}"
Sie können einen von mir erstellten Shell-Helfer verwenden: https://github.com/philpraxis/ipextract
haben sie hier der Bequemlichkeit halber beigefügt:
#!/bin/sh
ipextract ()
{
egrep --only-matching -E '(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'
}
ipextractnet ()
{
egrep --only-matching -E '(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/[[:digit:]]+'
}
ipextracttcp ()
{
egrep --only-matching -E '[[:digit:]]+/tcp'
}
ipextractudp ()
{
egrep --only-matching -E '[[:digit:]]+/udp'
}
ipextractsctp ()
{
egrep --only-matching -E '[[:digit:]]+/sctp'
}
ipextractfqdn ()
{
egrep --only-matching -E '[a-zA-Z0-9]+[a-zA-Z0-9\-\.]*\.[a-zA-Z]{2,}'
}
Laden Sie es (wenn es in der ipextract-Datei gespeichert ist) von Shell:
$. ipextract
Benutze sie:
$ ipextract < /etc/hosts
127.0.0.1
255.255.255.255
$
Für ein Beispiel eines echten Einsatzes:
ipextractfqdn < /var/log/snort/alert | sort -u
dmesg | ipextractudp
Sie können sed verwenden. Aber wenn Sie Perl kennen, könnte das einfacher und auf lange Sicht nützlicher sein:
Perl -n '/(\d+\.\d+\.\d+\.\d+)/ && print "$1\n"' < file
Sie könnten auch awk verwenden. So etwas wie ...
awk '{i = 1; if (NF> 0) do {if ($ i ~/regexp /) print $ i; i ++;} while (i <= NF);} 'Datei
- kann gereinigt werden. Nur eine schnelle und schmutzige Antwort, um im Grunde zu zeigen, wie man es mit awk macht
Ich habe alle Antworten ausprobiert, aber alle hatten ein oder viele Probleme, die ich einige auflistete.
123.456.789.111
als gültige IP-Adresse 127.0.00.1
nicht als gültige IP-Adresse 08.8.8.8
Also poste ich hier einen Regex, der unter allen oben genannten Bedingungen funktioniert.
Hinweis: Ich habe mehr als 2 Millionen IP-Adressen problemlos mit folgendem Regex extrahiert.
(?:(?:1\d\d|2[0-5][0-5]|2[0-4]\d|0?[1-9]\d|0?0?\d)\.){3}(?:1\d\d|2[0-5][0-5]|2[0-4]\d|0?[1-9]\d|0?0?\d)
Alle vorherigen Antworten haben ein oder mehrere Probleme. Die akzeptierte Antwort erlaubt IP-Nummern wie 999.999.999.999. Für die derzeit am zweithöchsten bewertete Antwort ist die Angabe von 0 wie 127.000.000.001 oder 008.008.008.008 anstelle von 127.0.0.1 oder 8.8.8.8 erforderlich. Apama hat es fast richtig, aber für diesen Ausdruck ist es erforderlich, dass die IP-Nummer das einzige in der Zeile ist. Es darf kein Leerzeichen vor- oder nachgestellt werden, und es kann auch keine IP aus der Mitte einer Zeile ausgewählt werden.
Ich denke, der korrekte Regex ist auf http://www.regextester.com/22 zu finden.
Wenn Sie also alle IP-Adressen aus einer Datei extrahieren möchten, verwenden Sie:
grep -Eo "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])" file.txt
Wenn Sie keine Duplikate wünschen, verwenden Sie:
grep -Eo "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])" file.txt | sort | uniq
Bitte kommentieren Sie, ob es noch Probleme in dieser Regex gibt. Es ist leicht, viele falsche Regex für dieses Problem zu finden. Ich hoffe, dieser hat keine echten Probleme.
Jeder hier verwendet sehr lange reguläre Ausdrücke, aber wenn Sie die Regex von POSIX verstehen, können Sie einen kleinen grep
-Befehl wie diesen zum Drucken von IP-Adressen verwenden.
grep -Eo "(([0-9]{1,3})\.){3}([0-9]{1,3})"
(Randnotiz) Ungültige IPs werden nicht ignoriert, aber es ist sehr einfach.
Für diejenigen, die eine fertige Lösung zum Abrufen von IP-Adressen aus dem Apache-Protokoll und zum Auflisten der Häufigkeit, mit der die IP-Adresse die Website besucht hat, wünschen, verwenden Sie diese Zeile:
grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' error.log | sort | uniq -c | sort -nr > occurences.txt
Schöne Methode, um Hacker zu verbieten. Als nächstes können Sie:
deny from
und ein Leerzeichen am Anfang jeder Zeile hinzuIch würde Perl vorschlagen. (\ d +.\d +.\d +.\d +) sollte den Trick wahrscheinlich tun.
EDIT: Um es eher wie ein komplettes Programm zu machen, können Sie Folgendes tun (nicht getestet):
#!/usr/bin/Perl -w
use strict;
while (<>) {
if (/(\d+\.\d+\.\d+\.\d+)/) {
print "$1\n";
}
}
Dies behandelt eine IP pro Leitung. Wenn Sie mehr als eine IP-Adresse pro Leitung haben, müssen Sie die Option/g verwenden. man perlretut gibt ein detailliertes Tutorial zu regulären Ausdrücken.