Comment extraire une partie de texte par expression rationnelle dans un shell linux? Disons que j'ai un fichier où chaque ligne est une adresse IP, mais à une position différente. Quel est le moyen le plus simple d'extraire ces adresses IP à l'aide d'outils de ligne de commande unix courants?
Vous pouvez utiliser grep pour les extraire.
grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' file.txt
La plupart des exemples ici correspondront à 999.999.999.999 qui n'est pas techniquement une adresse IP valide.
Les éléments suivants ne concordent qu'avec les adresses IP valides (y compris les adresses réseau et de diffusion).
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
Omettez le -o si vous voulez voir toute la ligne qui correspond.
Je commence habituellement par grep, pour obtenir l'expression rationnelle correcte.
# [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
Ensuite, j'essayerais de le convertir en sed
pour filtrer le reste de la ligne. (Après avoir lu ce fil de discussion, vous et moi n'allons plus le faire: nous allons utiliser grep -o
à la place)
sed -ne 's/.*\([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\).*/\1/p # FAIL
C'est à ce moment-là que je m'énerve généralement avec sed
pour ne pas utiliser les mêmes expressions rationnelles que quiconque. Je passe donc à Perl
.
$ Perl -nle '/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/ and print $&'
Perl est bon à savoir dans tous les cas. Si vous avez un petit morceau de CPAN installé, vous pouvez même le rendre plus fiable à moindre coût:
$ Perl -MRegexp::Common=net -nE '/$RE{net}{IPV4}/ and say $&' file(s)
Cela fonctionne bien pour moi dans les journaux d'accès.
cat access_log | egrep -o '([0-9]{1,3}\.){3}[0-9]{1,3}'
Brisons-le partie par partie.
[0-9]{1,3}
signifie une à trois occurrences de la plage mentionnée dans []. Dans ce cas, il est 0-9. il correspond donc à des motifs tels que 10 ou 183.
Suivi d'un '.' Nous devrons échapper à cela en tant que '.' est un méta-caractère et a une signification particulière pour Shell.
Alors maintenant, nous sommes à des modèles comme «123». '12. ' etc.
Ce motif se répète trois fois (avec le '.'). Donc nous le mettons entre parenthèses .([0-9]{1,3}\.){3}
Et enfin le motif se répète mais cette fois sans le '.'. C'est pourquoi nous l'avons gardé séparément dans la 3ème étape. [0-9]{1,3}
Si les ips sont au début de chaque ligne comme dans mon cas, utilisez:
egrep -o '^([0-9]{1,3}\.){3}[0-9]{1,3}'
où '^' est une ancre qui indique de rechercher en début de ligne.
J'ai écrit un peu script pour mieux voir mes fichiers de log, ce n'est pas spécial, mais cela pourrait aider beaucoup de gens qui apprennent Perl. Il effectue des recherches DNS sur les adresses IP après les avoir extraites.
J'ai écrit un article de blog informatif sur ce sujet: Comment extraire des adresses IP IPv4 et IPv6 d'un texte brut à l'aide de Regex .
Dans cet article, vous trouverez un guide détaillé des différents modèles les plus courants pour les IP, qui doivent souvent être extraits et isolés du texte brut à l'aide d'expressions régulières.
Ce guide est basé sur l'outil de code source IP Extractor de CodVerter permettant de gérer l'extraction et la détection d'adresses IP si nécessaire .
Si vous souhaitez valider et capturer l'adresse IPv4, ce modèle peut faire l'affaire:
\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
ou pour valider et capturer une adresse IPv4 avec Prefix ("notation par barre oblique"):
\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
ou pour capturer un masque de sous-réseau ou un masque générique:
(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)
ou pour filtrer les adresses de masque de sous-réseau vous le faites avec regex lookahead négatif :
\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
Pour la validation IPv6, vous pouvez aller au lien d'article que j'ai ajouté en haut de cette réponse.
Voici un exemple pour capturer tous les modèles courants (extraits de l'exemple d'aide de CodVerter sur IP Extractor):
Si vous le souhaitez, vous pouvez tester la regex IPv4 ici .
grep -E -o "([0-9] {1,3} [.]) {3} [0-9] {1,3}"
Vous pouvez utiliser l’assistant Shell que j’ai fabriqué: https://github.com/philpraxis/ipextract
inclus ici pour plus de commodité:
#!/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,}'
}
Chargez-le/source-le (lorsqu'il est stocké dans un fichier ipextract) à partir de Shell:
$. ipextract
Utilise les:
$ ipextract < /etc/hosts
127.0.0.1
255.255.255.255
$
Pour quelques exemples d'utilisation réelle:
ipextractfqdn < /var/log/snort/alert | sort -u
dmesg | ipextractudp
Vous pouvez utiliser sed . Mais si vous connaissez Perl, cela pourrait être plus facile et plus utile de savoir à long terme:
Perl -n '/(\d+\.\d+\.\d+\.\d+)/ && print "$1\n"' < file
Vous pouvez aussi utiliser awk. Quelque chose comme ...
awk '{i = 1; if (NF> 0) fait {if ($ i ~/regexp /) print $ i; i ++;} while (i <= NF);} 'fichier
- peut avoir besoin de nettoyage. juste une réponse rapide et sale pour montrer fondamentalement comment le faire avec awk
J'ai essayé toutes les réponses mais toutes ont eu un ou plusieurs problèmes et j'en ai énuméré quelques-unes.
123.456.789.111
comme IP valide 127.0.00.1
comme IP valide 08.8.8.8
Donc ici je poste une regex qui fonctionne dans toutes les conditions ci-dessus.
Note: J'ai extrait plus de 2 millions d'IP sans aucun problème avec les expressions régulières suivantes.
(?:(?: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)
Toutes les réponses précédentes ont un ou plusieurs problèmes. La réponse acceptée autorise les numéros IP tels que 999.999.999.999. La deuxième réponse la plus votée actuellement nécessite de préfixer 0, par exemple 127.000.000.001 ou 008.008.008.008 au lieu de 127.0.0.1 ou 8.8.8.8. Apama a presque tout à fait raison, mais cette expression nécessite que l'ipnumber soit la seule chose sur la ligne, aucun espace de début ou de fin n'est autorisé, pas plus qu'il ne peut sélectionner d'ip au milieu d'une ligne.
Je pense que la bonne expression rationnelle peut être trouvée sur http://www.regextester.com/22
Donc, si vous voulez extraire toutes les adresses IP d'un fichier, utilisez:
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
Si vous ne voulez pas de doublons, utilisez:
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
Veuillez commenter s'il y a encore des problèmes dans cette regex. Il est facile de trouver beaucoup de mauvaises expressions rationnelles pour ce problème, j'espère que celui-ci n'a pas de problèmes réels.
Tout le monde ici utilise des expressions régulières très longues, mais comprendre réellement l'expression régulière de POSIX vous permettra d'utiliser une petite commande grep
comme celle-ci pour imprimer les adresses IP.
grep -Eo "(([0-9]{1,3})\.){3}([0-9]{1,3})"
(Note latérale) Cela n'ignore pas les IP invalides, mais c'est très simple.
Pour ceux qui veulent une solution prête pour obtenir les adresses IP à partir du journal Apache et répertoriant le nombre de fois où l'adresse IP a visité le site Web, utilisez cette ligne:
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
Belle méthode pour interdire les pirates. Ensuite, vous pouvez:
deny from
et un espace au début de chaque ligneJe suggérerais Perl. (\ d +.\d +.\d +.\d +) devrait probablement faire l'affaire.
EDIT: Juste pour que cela ressemble plus à un programme complet, vous pourriez faire quelque chose comme ceci (non testé):
#!/usr/bin/Perl -w
use strict;
while (<>) {
if (/(\d+\.\d+\.\d+\.\d+)/) {
print "$1\n";
}
}
Cela gère une adresse IP par ligne. Si vous avez plusieurs adresses IP par ligne, vous devez utiliser l'option/g. man perlretut vous fournit un didacticiel plus détaillé sur les expressions régulières.