Comment analyser un fichier journal multiligne dans awk et ne générer qu'une seule ligne avec la dernière adresse IP connue

0

Je suis coincé et cherche de l'aide. Je souhaite déclencher un événement que je souhaiterais traiter plus avant à l'aide d'un script bash. Les données sont extraites d'un fichier journal. Avant de commencer à expliquer, je vais vous montrer quelques lignes de ce fichier journal pour une meilleure compréhension.

À quoi il ressemble

test.log

[...]
24/04/2017 20:14:29 [ 7910] [INFO] [bob] method='POST' from='192.168.0.163' getUser='bob' some other colums
24/04/2017 20:14:34 [10355] [INFO] [bob] method='POST' from='192.168.0.163' getUser='bob' some other colums
24/04/2017 20:14:38 [10355] [INFO] [bob] Processed '1' incoming changes
24/04/2017 20:14:47 [22518] [INFO] [bob] method='POST' from='192.168.0.163' getUser='bob' some other colums
24/04/2017 20:14:50 [ 7910] [INFO] [bob] method='POST' from='192.168.0.163' getUser='bob' some other colums
24/04/2017 20:14:53 [ 7910] [INFO] [bob] Processed '1' incoming changes
24/04/2017 20:15:08 [10355] [INFO] [bob] method='POST' from='192.168.0.151' getUser='bob' some other colums
24/04/2017 20:15:14 [22518] [INFO] [bob] method='POST' from='192.168.0.151' cmd='Search' getUser='bob' some other colums
24/04/2017 20:15:15 [ 7910] [INFO] [bob] method='POST' from='192.168.0.151' getUser='bob' some other colums
24/04/2017 20:15:16 [10355] [INFO] [bob] method='POST' from='192.168.0.151' cmd='Search' getUser='bob' some other colums
24/04/2017 20:15:49 [32637] [INFO] [bob] method='POST' from='192.168.0.163' getUser='bob' some other colums
24/04/2017 20:15:53 [22518] [INFO] [bob] method='POST' from='192.168.0.163' getUser='bob' some other colums
24/04/2017 20:15:56 [22518] [INFO] [bob] Processed '1' incoming changes
24/04/2017 20:16:05 [10355] [INFO] [bob] method='POST' from='192.168.0.151' getUser='bob' some other colums
24/04/2017 20:16:09 [32637] [INFO] [bob] method='POST' from='192.168.0.151' getUser='bob' some other colums
01/05/2017 03:27:45 [ 4985] [INFO] [alice] method='POST' from='192.168.0.153' getUser='alice' some other colums
01/05/2017 03:27:49 [13971] [INFO] [alice] method='POST' from='192.168.0.153' getUser='alice' some other colums
01/05/2017 03:28:05 [13970] [INFO] [alice] method='POST' from='192.168.0.153' getUser='alice' some other colums
01/05/2017 03:28:10 [ 4985] [INFO] [alice] method='POST' from='192.168.0.153' getUser='alice' some other colums
01/05/2017 03:28:25 [13971] [INFO] [alice] method='POST' from='192.168.0.153' getUser='alice' some other colums
01/05/2017 03:28:31 [13970] [INFO] [alice] method='POST' from='192.168.0.153' getUser='alice' some other colums
15/03/2018 14:49:19 [12918] [INFO] [alice] method='POST' from='192.168.0.171' getUser='alice' some other colums
15/03/2018 14:49:21 [12834] [INFO] [alice] method='POST' from='192.168.0.171' getUser='alice' some other colums
15/03/2018 14:49:22 [12834] [INFO] [alice] SyncCollections->CheckForChanges(): Waiting for store changes... (lifetime 470 seconds)
15/03/2018 14:55:26 [12843] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' some other colums
15/03/2018 14:55:26 [12918] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' some other colums
15/03/2018 14:55:26 [12882] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' some other colums
15/03/2018 14:55:27 [12970] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' some other colums
15/03/2018 14:55:28 [12882] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' some other colums
15/03/2018 14:55:28 [12918] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' some other colums
15/03/2018 14:55:32 [12970] [INFO] [bob] method='POST' from='192.168.0.166' getUser='bob' some other colums
15/03/2018 14:55:32 [12970] [INFO] [bob] SyncCollections->CheckForChanges(): Waiting for store changes... (lifetime 470 seconds)
[...]

Objectif

Je souhaite récupérer le nom d'utilisateur (dans cet exemple, "alice" ou "bob") à partir du fichier journal qui apparaît dans la cinquième colonne et l'adresse IP appropriée qui est répertoriée dans la septième colonne. Si l'adresse IP diffère de l'état précédent, une notification par courrier électronique doit être envoyée via un petit script bash.

La condition devrait être:

  • si la ligne contient "alice" OU "bob" ET la ligne contient "from =" puis indiquez le nom d'utilisateur et l'adresse IP appropriée.

La sortie finale devrait ressembler à

bob 192.168.0.166
alice 192.168.0.171

Remarque: Seule la dernière adresse IP connue est recherchée. Par conséquent, la sortie générée correctement ne doit générer que 2 lignes dans cet exemple, comme indiqué ci-dessus (une par utilisateur).

Ce que j'ai essayé jusqu'à présent

J'ai commencé avec awk mais a rapidement fait face à un obstacle, car awk utilise par défaut l’espace blanc comme séparateur de champ. Mon intention était de commencer par une déclaration '{print $ 4, $ 6}}. J'ai réalisé que la troisième colonne casse parfois ce filtrage à cause d'un espace de tête dans l'identificateur de processus, par exemple.

24/04/2017 20:14:50 [ 7910] ...

À quoi ressemble ma commande awk

Avec la commande suivante, je cherche la chaîne "alice" OU "bob" ET la chaîne "from =", puis génère une sortie de deux colonnes non formatées.

awk 'BEGIN { FS = "[?!([ )]+" } /alice|bob/ && /from=/ { print $5,$7 }' test.log

Sortie - & gt;

bob] from='192.168.0.163'
bob] from='192.168.0.163'
bob] from='192.168.0.163'
bob] from='192.168.0.163'
bob] from='192.168.0.151'
bob] from='192.168.0.151'
bob] from='192.168.0.151'
bob] from='192.168.0.151'
bob] from='192.168.0.163'
bob] from='192.168.0.163'
bob] from='192.168.0.151'
bob] from='192.168.0.151'
alice] from='192.168.0.153'
alice] from='192.168.0.153'
alice] from='192.168.0.153'
alice] from='192.168.0.153'
alice] from='192.168.0.153'
alice] from='192.168.0.153'
alice] from='192.168.0.171'
alice] from='192.168.0.171'
bob] from='192.168.0.166'
bob] from='192.168.0.166'
bob] from='192.168.0.166'
bob] from='192.168.0.166'
bob] from='192.168.0.166'
bob] from='192.168.0.166'
bob] from='192.168.0.166'

Je suis coincé ici. J'ai essayé de jouer en stockant la dernière ligne connue dans une variable et en indiquant "{a = $ 0}" mais, de toute évidence, je fais quelque chose de mal car je reçois des erreurs ou le résultat est faux. Mon idée suivante était d'utiliser "tac" et de commencer à lire le fichier journal à partir de sa fin, puis de sortir après le premier match. Quelque chose comme ca:

tac test.txt | awk 'BEGIN { FS = "[?!([ )]+" } /alice|bob/ && /from=/ { print $5,$7; exit }'

mais cela s'arrête immédiatement après le 1er match et la sortie est:

bob] from='192.168.0.166'

J'ai également besoin de sortir le formatage en supprimant le crochet droit ']', la chaîne 'from =' et les guillemets simples autour de l'adresse IP.

Toute aide vraiment appréciée. Merci d'avance.

user882786
la source

Réponses:

0

Vous pouvez étendre votre séparateur de champ regex pour inclure ] et ' et alors vous aurez le nom et l'IP proprement dans les champs 5 et 9. Vous pouvez les sauvegarder dans un tableau associatif indexé par le nom et contenant la dernière adresse IP. En fin de fichier, vous imprimez ce tableau.

awk 'BEGIN { FS = "[?!([ )\\]'\'']+" }
/alice|bob/ && /from=/ { 
    user = $5; ip = $9;
    userip[user] = ip
}
END{ for(user in userip)print user,userip[user] }'
meuh
la source
0

Bonjour meuh et merci beaucoup pour votre suggestion avec exemple. Cela fonctionne très bien. Mais je me demande toujours s'il ne serait pas préférable d'inverser le traitement et de commencer à lire à partir de la fin du fichier. Parce que dans ce cas, si le fichier journal à lire contient des milliers de lignes, il consomme beaucoup de puissance de traitement. J'imagine qu'il serait plus efficace pour performace de commencer à lire à partir de la queue et de s'arrêter après le premier match pour chaque utilisateur.

D'autre part, je me demande s'il est possible d'inclure tout mon projet dans awk en tant que ligne unique.

L'objectif est d'exécuter une tâche cron toutes les minutes et de lire le fichier journal. Si l'adresse IP a changé et est plus récente que la dernière adresse connue et que le sous-réseau ip ne se trouve pas dans le sous-réseau C (LAN), la notification par courrier électronique doit être envoyée.

/etc/cron.d/access-audit.log

*/1 * * * * root nice -n5 /usr/bin/awk 'BEGIN { FS = "[?!([ )\]'\'']+" } /alice|bob/ && /from=/ { user = $5; ip = $9; userip[user] = ip } END{ for(user in userip)print user,userip[user] }' | ...

Je ne sais pas comment y arriver. Dois-je toucher un fichier de drapeau dans lequel je stocke l'adresse IP actuelle de chaque utilisateur, puis l'interroger ultérieurement? Est-il possible de tout faire dans awk?

user882786
la source