Forum Programmation.shell awk : imbrications de commandes

Posté par . Licence CC by-sa
Tags :
1
14
jan.
2015

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 . Évalué à 2.

    Je souhaiterais me mettre au awk, car on me dit que c'est plus performant qu'une succession de cut et sed.

    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).

    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.

    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 . Évalué à 1. Dernière modification le 14/01/15 à 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.

      Dec 28 04:15:04 $$$##$.$$$$$.$$$$$ apache[#####]: 66.249.67.xxx - - [28/Dec/2014:04:15:04 +0100] "GET /url HTTP/1.0" 302 224 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" www.$$$$$$$$$$$$$$$$.fr 89 "-" 

      Ce que je veux :

      28Dec2014|###.###.###.xxx|/url|89|www.$$$$$$$$$.fr|302|-|Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)|-

      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) :

      Dec 28 04:15:04 $$$##$.$$$$$.$$$$$ apache[#####]: 66.249.67.xxx - - [28/Dec/2014:04:15:04 +0100] "GET /url HTTP/1.0" 304 - "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" www.$$$$$$$$$$$$$$$$.fr 9476 "Mon, 22 Dec 2014 18:38:26 GMT" 

      Encore une (avec referer google.it et pas de last modified) :

      Dec 28 04:15:06 $$$##$.$$$$$.$$$$$ apache[#####]: 66.249.67.xxx - - [28/Dec/2014:04:15:06 +0100] "GET /url HTTP/1.0" 200 33202 "https://www.google.it/" "Mozilla/5.0 (iPad; CPU OS 7_1_2 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D257 Safari/9537.53" www.$$$$$$$$$$$$$$$$.fr 522265 "-" 

      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 . Évalué à 2.

        Je tiens à cacher l'ip/domaine et d'autres infos, je ne pense pas que cela change quelque chose.

        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.

        ps2 : rha et si on peut transformer les JAN FEV etc en 01 02 ^

        ç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 . Évalué à 1. Dernière modification le 14/01/15 à 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 . Évalué à 3.

            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 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 ;-)

            tu confonds ptet avec la ligne de ce que je souhaiterais en print

            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 . Évalué à 1. Dernière modification le 14/01/15 à 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) :

        Dec 28 04:15:04 $$$##$.$$$$$.$$$$$ apache[#####]: 66.249.67.xxx - - [28/Dec/2014:04:15:04 +0100] "GET /url HTTP/1.0" 304 - "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" www.$$$$$$$$$$$$$$$$.fr 9476 "Mon, 22 Dec 2014 18:38:26 GMT" 
        date|ip|get|temps_chargement|domaine|res_code|referer|user_agent|last_modified
        28Dec2014|66.249.67.xxx|/url|9476|www.$$$$$$$$$$$$$$$$.fr|304|-|Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)|22Dec2014

        Encore une (avec referer google.it et pas de last modified) :

        Dec 28 04:15:06 $$$##$.$$$$$.$$$$$ apache[#####]: 66.249.67.xxx - - [28/Dec/2014:04:15:06 +0100] "GET /url HTTP/1.0" 200 33202 "https://www.google.it/" "Mozilla/5.0 (iPad; CPU OS 7_1_2 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D257 Safari/9537.53" www.$$$$$$$$$$$$$$$$.fr 522265 "-" 
        date|ip|get|temps_chargement|domaine|res_code|referer|user_agent|last_modified
        28Dec2014|66.249.67.xxx|/url|522265|www.$$$$$$$$$$$$$$$$.fr|200|https://www.google.it/|Mozilla/5.0 (iPad; CPU OS 7_1_2 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D257 Safari/9537.53|-
    • [^] # Re: awk ou pas awk, c'est la question

      Posté par . É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 :)

  • # doc sur awk

    Posté par (page perso) . É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

    If you choose open source because you don't have to pay, but depend on it anyway, you're part of the problem.evloper) February 17, 2014

    • [^] # Re: doc sur awk

      Posté par . É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 . É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 . Évalué à 1.

          Merci pour le tips python :)

          Mais ouep je souhaite apprendre awk

      • [^] # Re: doc sur awk

        Posté par . Évalué à 2.

        awk le fait très bien : fonction split().

        • [^] # Re: doc sur awk

          Posté par . Évalué à 1.

          Merci, grand merci.
          Ca fait exactement ce que je voulais.
          Je ne connaissais pas.

          • [^] # Re: doc sur awk

            Posté par . É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 à ceux qui les ont postés. Nous n'en sommes pas responsables.