Bonjour à toutes et à tous :)
Je présente tout d'abord mon faible niveau en programmation, afin que vous puissiez adapter vos réponses, à mon niveau de compréhension :)
J'ai un niveau débutante en python (fonctions, boucles, listes…), php (pas script mais plutôt interrogation avec base sql), je me débrouille bien en expressions régulières, en cut et sed. Me sont inconnus c, js, javaj, c++.
J'avance dans la douleur, et n'ai pas forcément le bon vocabulaire pour trouver réponse sur Google. Je suis également de l'école : je ne demande jamais d'aide, quitte à passer plusieurs jours sur un point virgule. Mais me voici.
Je souhaiterais me mettre au awk, car on me dit que c'est plus performant qu'une succession de cut et sed.
Ayant un peu de temps, j'ai décidé de m'y mettre en réalisant un programme qui me parserait des logs serveur web. On y retrouve donc des informations telles que :
date, heure, ip, méthode, url, user agent, referer, temps de chargement de la page…
J'ai réalisé un awk qui me ressort des champs de mes fichiers, que je souhaite séparer par des | afin de les traiter facilement par la suite.
Dans mon fichier que je lis, j'utilise comme séparateurs l'espace et le guillemet.
Mon programme :
Je reformate la date (gensub) puis je l'affiche. La boucle me sert à sortir le user agent, puis j'affiche les autres champs de manière classique.
J'utilise une boucle pour m'extraire le "user agent", car ce champ contient des espaces (et c'est aussi mon séparateur). La boucle me permet d'extraire un champs dans un intervalle : entre le champs X et X-N.
Sauf que, il y a un autre champs à extraire, qui contient lui aussi des expaces. Du coup, mon awk ne fonctionne plus : ni pour extraire le user agent, ni pour extraire cet autre champs : las modified (de type "TUE, 23 Dev 2015 GMT")
Ce que je fais, en mode débrouille, c'est un sed 's/[A-Z][a-z][a-z], ([0-9][0-9]) ([A-Z][a-z][a-z]) ([0-9][0-9][0-9][0-9]) [0-9][0-9]:[0-9][0-9]:[0-9][0-9] GMT/\1\2\3/'
Afin de nettoyer le fichier, puis d'y appliquer mon awk.
Je souhaiterais tout faire avec mon awk. Je cherche donc à faire un gensub sur ce champs problématique pour remplacer les espaces par des tirets, créer un fichier temporaire, sur lequel j'appliquerai mon awk. OU ALORS. Faire un gensub et appliquer mon awk sur le résultat du gensub, le tout en mémoire. Cette dernière solution poserait des problèmes de lenteur, car mes fichiers à parser peuvent faire plusieurs Go.
Je ne sais pas en awk créer un fichier temporaire de travail, et le réutiliser pour enchainer une nouvelle commande.
Ou alors utiliser le | pour enchainer des commandes ? Mais on travaille en mémoire non ? Ca risque de saturer.
Je laisse mon chef d'oeuvre en bas de mon message.
Je vous remercie bien d'avance.
Marie
BEGIN {
print "date|ip|get|temps_chargement|domaine|res_code|referer|user_agent|last_modified"
}
{
FS=" |\""
a=gensub(/\[([0-9][0-9])\/([A-Z][a-z][a-z])\/([0-9]{4}):[0-9][0-9]:[0-9][0-9]:[0-9][0-9]/, "\\1\\2\\3","g",$9); #date
str=""
for (x=20; x<=NF-6; x++) { str=str$x }; #user_agent
print a"|"$6"|" $13"|" $(NF-4)"|" $(NF-5)"|" $16"|"$19"|"str"|"$(NF-2) #date ip get chargement domaine res_code referer user_agent lastmodified
}
Une ligne de log ressemble à ca (passage Googlebot) :
Dec 28 04:15:04 champs1 apache[dddddd]: dd.dd.dd.xxx - - [28/Dec/2014:04:15:04 +0100] "GET /url-web/prive-blabla HTTP/1.0" rescode - "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" www.mondomaine.fr tempschargementenchiffres "Mon, 22 Dec 2014 18:38:26 GMT"
Si un champ est vide, il est remplacé par un tiret.
# awk ou pas awk, c'est la question
Posté par gaaaaaAab . Évalué à 2.
mouais. S'il y a des invocations multiples de cut et sed pour chaque ligne du fichier, forcément, awk risque d'être plus rapide. Mais si les cut et sed sont factorisables pour avoir une seule invocation de cut et sed pour traiter l'intégralité du ficher, c'est beaucoup moins sûr.
Pour ma part, si je dois choisir entre awk et sed, ce n'est pas sur le critère performance, mais plutôt sur le type de traitement. Quand il faut traiter du flux, j'utilise quasiment toujours sed/cut. awk permet plus facilement d'ajouter des traitements globaux dans le bloc END, ou d'appliquer des traitements sur des blocs multilignes (mais c'est possible de faire des trucs de ouf en sed). Sachant qu'en vrai, si je dois effectuer un traitement suffisamment complexe pour nécessiter un bloc END, je vais assez rapidement basculer sur un langage de script plus évolué (genre Python).
Cela dit, se frotter à des langages qu'on ne connait pas, c'est jamais du temps perdu.
Par rapport à ta question, pourrais-tu donner une "vraie" ligne d'exemple (par exemple en n'anonymisant que le nom de domaine et l'IP) ? (Pourquoi remplacer des valeurs numériques par "tempschargementenchiffres" ou rescode ?).
Et peux-tu également mettre la ligne que tu cherches à obtenir après traitement ? En appliquant ton traitement chez moi (sed puis awk), dans la ligne que j'obtiens, le contenu des champs ne collent pas à leur titre, du coup, je sais que ce que j'obtiens ne correspond pas à ce que tu attends (et donc je ne sais pas exactement ce que tu attends).
oui, c'est bien le rôle de | d'enchaîner des commandes. Il me semble que awk n'a pas besoin de charger tout le ficher. Il travaille en flux (dés qu'il peut traiter un bloc - ici, une ligne - il le fait). Dans ton traitement, tu alloues des variables locales de type simple, réutilisées à chaque ligne, du coup, la consommation mémoire ne devrait pas dépendre de la taille du fichier. Je ne vois pas de problème de ce côté là.
ps: à la relecture, je me rends compte que depuis le temps que je lis linuxfr, j'ai tendance à vite rentrer dans le vif du sujet, ambiance "comme à la maison". Ne t'en formalise pas trop et bienvenue !
[^] # Re: awk ou pas awk, c'est la question
Posté par fycloud . Évalué à 1. Dernière modification le 14 janvier 2015 à 13:18.
test je me prends des erreurs 500 :(
soit parce que ma réponse est trop longue (exemples de lignes) soit qu'il ne prend pas certains caractères spéciaux
Bonjour et merci de ta réponse, rapide.
Je tiens à cacher l'ip/domaine et d'autres infos, je ne pense pas que cela change quelque chose. Je suis désolée mais ce sont des infos que je ne peux pas divulguer. Même en laissant l'url en get, on arrive à retrouver de quel site il s'agit, et pour qui je travaille…
Je remplace lettre par $, chiffre par #
le "xxx" à la fin de l'ip est ce que l'on récupère de notre côté : pas le droit de récupérer / stocker cette donnée des internautes. On remplace par des xxx. On récupère directement ça sur les serveurs.
Ce que je veux :
En fait le rendu dépend de la ligne, car toutes les lignes n'ont pas forcément de referer ou de lastmodified rempli. Le last modified n'est présent que lorsqu'il s'agit d'un bot Google
Une autre ligne de log (ici avec lastmodified = Mon, 22 Dec 2014 18:38:26 GMT que je souhaite sous ce format là : 22Dec2014) :
Encore une (avec referer google.it et pas de last modified) :
Mon rendu contient tous les champs :
date|ip|get|temps_chargement|domaine|res_code|referer|user_agent|last_modified
PS : ne t'en fais pas !
Même si la dernière fois que j'ai posté sur forum c'était il y a 15 ans, j'ai l'habitude d'en lire :)
ps2 : rha et si on peut transformer les JAN FEV etc en 01 02 ^
[^] # Re: awk ou pas awk, c'est la question
Posté par gaaaaaAab . Évalué à 2.
Pas de problème. C'était le remplacement du status HTML par rescode et du temps de chargement par tempschargementenchiffres dans ton exemple qui me faisait tiquer. Par contre, désolé de pinailler, mais il y a encore un truc qui m'échappe. Dans le résultat attendu, le deuxième champ est ###.###.###.xxx, mais cette chaine de caractères n'est pas présente dans la ligne de donnée (à moins que ça soit mangé par le formattage du site).
Pour l'anonymisation, /url et mondomaine.fr me vont très bien. Pour anonymiser les adresses ip, tu pourrais utiliser une IP bidon genre 1.2.3.4 plutôt que des #. ça permettrait de s'y retrouver plus facilement, et surtout, ça serait une valeur du même type que la vraie donnée.
ça, c'est p-e la partie la plus facile de la question :) Tu pourrais initialiser un tableau associatif (comme un dict en Python) dans le bloc BEGIN pour écrire les associations entre les mois et leur nombre, et t'en servir dans le print.
[^] # Re: awk ou pas awk, c'est la question
Posté par fycloud . Évalué à 1. Dernière modification le 14 janvier 2015 à 14:14.
Ravie d'apprendre que c'est le plus facile, je vais le faire dans le begin, mais est-ce que ça sera bien pris en compte lorsque je ferai mon gsub derrière ?
j'ai mis un ip de Googlebot, elle est publique. Je comprends ton pinaillage ne t'en fais pas. Les bons devs sont pinailleurs. C'est bon signe.
Il y a des lignes ou l'ip n'est effectivement pas présente. C'est alors remplacé par un -
Mais ce n'ets pas le cas dans les exemples que j'ai envoyés (tu confonds ptet avec la ligne de ce que je souhaiterais en print)
[^] # Re: awk ou pas awk, c'est la question
Posté par gaaaaaAab . Évalué à 3.
j'ai peut-être grillé quelques étapes, je développe. Tu peux déclarer un tableau associatif dans le bloc BEGIN, qui sera ensuite une variable globale. Tu pourrais aussi le déclarer dans le bloc principal, mais il serait alors ré initialisé à chaque ligne, ce qui coute des performances. Ensuite, non, la traduction des JAN/FEB en 01/02 n'est pas "magique", il faudra bien utiliser ce tableau dans le bloc principal pour effectuer le remplacement. Mais je t'en laisse un petit peu à faire ;-)
Je ne confonds pas, mais c'est bien de ça dont je parle. Si tu mets 3 lignes de données d'exemple, il faudrait aussi les 3 lignes de résultats attendus correspondant précisément à ces exemples. Pour toi, c'est peut-être évident que ###.###.### du résultat attendu correspond à tel truc pour la première ligne de donnée, tel autre pour la seconde ou '-' pour la troisième. De mon point de vue, avoir une ligne de résultat avec un truc que je ne vois pas dans les données, c'est une source d’ambiguïté.
[^] # Re: awk ou pas awk, c'est la question
Posté par fycloud . Évalué à 1. Dernière modification le 14 janvier 2015 à 15:26.
je reprends mes deux autres sources où il n'y a pas le rendu.
(le lastmodified sera toujours le dernier champs entre guillements)
Ah une autre précision, quand je dis si un champs est vide il est remplacé par un tiret : c'est dans le fichier log qu'on le voit. Ce n'est pas moi qui le remplace ^
Une autre ligne de log (ici avec lastmodified = Mon, 22 Dec 2014 18:38:26 GMT que je souhaite sous ce format là : 22Dec2014) :
Encore une (avec referer google.it et pas de last modified) :
[^] # Re: awk ou pas awk, c'est la question
Posté par fycloud . Évalué à 1.
Ah pour justifier l'emploi du awk : je souhaiterais faire des opérations sur ce fichier par la suite.
Et bon, ça m'oblige à faire un peu de script, et me permettrai d'appréhender des choses un peu plus compliquées quand j'en aurai besoin :)
[^] # Re: awk ou pas awk, c'est la question
Posté par max22 . Évalué à 1.
je ne vois pas trop en quoi ça justifie l'emploi de awk
# doc sur awk
Posté par palm123 (site web personnel) . Évalué à 2.
la réponse précédente donnait un pointeur sur sed, et sur le même site, je te recommande les articles sur awk
http://www.catonmat.net/blog/awk-one-liners-explained-part-one/
(il y a 5 articles, tu cliques sur part 1, part 2…)
sinon tu dis
"Sauf que, il y a un autre champ à extraire, qui contient lui aussi des expaces. Du coup, mon awk ne fonctionne plus "
avec awk, -F indique le séparateur que tu veux, espace,",:,| ou autre
Bon par contre, pendant ton awk, je ne pense pas que tu puisses avoir différents séparateurs
ウィズコロナ
[^] # Re: doc sur awk
Posté par fycloud . Évalué à 1.
Bonjour,
Merci pour les liens.
Dans awk le FS=" |\"" fonctionn, il dit bien que mon séparateur est l'espace ou le guillement.
Ou alors trouver une solution pour d'abord parser sur le séparateur espace, puis le séparateur guillemet ?
J'avour que ça résoudrait le problème :)
[^] # Re: doc sur awk
Posté par max22 . Évalué à 1.
Ca risque d'être compliqué avec awk.
j'ai regardé vite fait, en python il existe une librairie pour parser les fichiers de log apache. (plus d'infos ici)
mais si ton but c'est d'apprendre awk c'est différent…
[^] # Re: doc sur awk
Posté par fycloud . Évalué à 1.
Merci pour le tips python :)
Mais ouep je souhaite apprendre awk
[^] # Re: doc sur awk
Posté par totof2000 . Évalué à 2.
awk le fait très bien : fonction split().
[^] # Re: doc sur awk
Posté par fycloud . Évalué à 1.
Merci, grand merci.
Ca fait exactement ce que je voulais.
Je ne connaissais pas.
[^] # Re: doc sur awk
Posté par totof2000 . Évalué à 2.
Pour info, j'ai appris awk en codant un script de résolution de sudoku … Ca m'a permis d'apprendre plein de choses.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.