Extraire les horodatages de plusieurs lignes dans un fichier journal avec des outils shell Unix

L’entrée est un fichier journal. Le processus qui m’intéresse actuellement enregistre une ligne au début et à la fin du processus. Le début a toujours un certain modèle fixe avec un ID d’object. La fin a également un motif fixe, avec le même ID d’object.

Je souhaite que la sortie contienne une seule ligne par ID d’object, suivie de l’horodatage de la première ligne, suivi de l’horodatage de la deuxième ligne. Cette sortie sera utilisée pour d’autres parsings dans d’autres outils. La sortie doit être sortingée sur l’horodatage de la ligne de départ; les objects sans lignes de départ (voir obstacles) doivent être placés à la fin.

Je voudrais résoudre ce problème en utilisant des outils shell Unix standard. Devinez, quelque chose avec awk devrait faire l’affaire. Si la solution implique un script shell Unix, veuillez utiliser sh comme shell.

Obstacles: Je ne peux pas garantir que le processus soit ssortingctement séquentiel, de sorte que le début de object1 peut être suivi par le début de object2 avant que object1 ait été entièrement traité. En outre, je ne peux pas garantir que le fichier journal correspond toujours à un début avec une fin, ou vice versa. Dans de tels cas, l’ID doit avoir une valeur vide pour le point manquant.

Les apparences d’entrée sont, par essence, quelque chose comme ceci:

2014-03-11 09:00:01.123 bla bla bla TAG_START ID:1234 bla bla bla 2014-03-11 09:00:11.123 bla bla bla TAG_END ID:1234 bla bla bla 2014-03-11 09:01:01.123 bla bla bla TAG_START ID:2353 bla bla bla 2014-03-11 09:02:01.123 bla bla bla TAG_END ID:2353 bla bla bla 2014-03-11 09:03:01.123 bla bla bla TAG_START ID:3456 bla bla bla 2014-03-11 09:04:01.123 bla bla bla TAG_END ID:4567 bla bla bla 

Sortie:

 1234;09:00:01.123;09:00:11.123 2353;09:01:01.123;09:02:01.123 3456;09:03:01.123; 4567;;09:04:01.123 

Merci d’avance!

Vous pouvez essayer quelque chose avec GNU awk (en utilisant la fonction asorti pour les sorties sortingées):

 gawk ' function findID(line) { for (i = 1; i<=NF; i++) if ($i ~ /^ID/) split($i, tmp, /:/) return tmp[2] } /TAG_START/ { id = findID($0) lines[id] = $2 ";" } /TAG_END/ { id = findID($0) lines[id] = ((lines[id]) ? lines[id] $2 : ";" $2) } END { n = asorti(lines, lines_s) for (i = 1; i <= n; i++) { print lines_s[i] ";" lines[lines_s[i]] } }' file 

Si vous n'avez pas GNU awk vous pouvez sort la sortie de awk standard.

 awk ' function findID(line) { for (i = 1; i<=NF; i++) if ($i ~ /^ID/) split($i, tmp, /:/) return tmp[2] } /TAG_START/ { id = findID($0) lines[id] = $2 ";" } /TAG_END/ { id = findID($0) lines[id] = ((lines[id]) ? lines[id] $2 : ";" $2) } END { for (x in lines) print x ";" lines[x] }' file | sort -t";" -nk1,2 

Sortie:

 1234;09:00:01.123;09:00:11.123 2353;09:01:01.123;09:02:01.123 3456;09:03:01.123; 4567;;09:04:01.123 

Explication:

  • Pour les lignes ayant /TAG_START/ nous appelons notre fonction définie par l'utilisateur qui parcourt chaque champ délimité par un espace. Une fois que nous rencontrons un champ qui commence par ID nous le divisons avec : delimiter et en capturons la deuxième partie (c'est-à-dire si le champ est TAG_START ID:1234 nous capturons 1234 ).
  • Nous l'utilisons comme clé dans nos lines tableau et lui atsortingbuons la valeur du second champ sur cette ligne, qui correspond à l'horodatage et au pad a ; après ça.
  • Nous faisons des actions similaires pour les lignes ayant /TAG_END/ seule différence étant donné que nous vérifions l'existence de la key dans notre tableau. S'il est présent, nous y ajoutons le second champ, car il s'agit de l'horodatage final. Si la clé n'est pas présente, nous ajoutons simplement et ajoutez la valeur au tableau. C'est pour répondre à vos besoins. De plus, je ne peux pas garantir que le fichier journal correspond toujours à un début avec une fin, ou vice versa. Dans de tels cas, l'ID doit avoir une valeur vide pour le point manquant.
  • Pour GNU awk nous appelons la fonction asorti pour sortinger par valeur et itérer sur le tableau et imprimer les lignes. Pour awk régulier, nous imprimons les lignes et les canalisons pour les sort .

La sortie sera dans le même ordre que les identifiants apparaissant dans votre entrée:

 awk -v OFS=';' ' { time = $2 type = (/TAG_START ID:/ ? "s" : "e") sub(/.*TAG_(START|END) ID:/,"") sub(/ .*$/,"") id = $0 if (!seen[id]++) { ids[++numIds] = id } times[id,type] = time } END { for (idNr=1; idNr<=numIds; idNr++) { id = ids[idNr] print id, times[id,"s"], times[id,"e"] } }' file 1234;09:00:01.123;09:00:11.123 2353;09:01:01.123;09:02:01.123 3456;09:03:01.123; 4567;;09:04:01.123 

L'instruction if fait que suivre les identifiants uniques dans l'ordre dans lequel ils sont vus dans le fichier d'entrée. La première fois qu'un identifiant est vu, le tableau seen[id] a la valeur zéro car c'est un nouvel identifiant unique et le compteur numIds est pré-incrémenté et l' id est stocké dans le tableau ids à la position indexée par la nouvelle valeur des numIds . Puisque seen[id] été post-incrémenté dans le if , la prochaine fois que cet id est vu seen[id] a la valeur 1 et donc la condition !seen[id] est maintenant false.

Il ne s'agit que de l'approche awk idiomatique de la façon de conserver une liste de clés uniques ( ids ) dans l'ordre dans lequel elles apparaissent dans l'entrée afin qu'elles puissent être référencées dans cet ordre dans la section END plutôt que dans l'ordre aléatoire.

Utilisez des arrays of arrays dans gnu awk.

 awk '{split($7,c,":");a[c[2]][$6]=$2;b[c[2]]} END{for (i in b) {print i,a[i]["TAG_START"],a[i]["TAG_END"]}}' OFS=";" file 1234;09:00:01.123;09:00:11.123 2353;09:01:01.123;09:02:01.123 3456;09:03:01.123; 4567;;09:04:01.123 

Explication

  • échantillon $ 7 est ID:1234 , divisé en tableau c et utilise la valeur c [2] comme index dans le tableau a.
  • avec des arrays of arrays , vous pouvez imprimer deux valeurs a[i]["TAG_START"] et a[i]["TAG_END"] directement

Nouvelle version si la position de l’ID n’est pas fixe.

 awk '{for (i=1;i<=NF;i++) if ($i ~/TAG_(START|END)/) {status=$i;id=$(i+1)};split(id,c,":");a[c[2]][status]=$2;b[c[2]]} END{for (i in b) {print i,a[i]["TAG_START"],a[i]["TAG_END"]}}' OFS=";" file