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:
/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
). lines
tableau et lui atsortingbuons la valeur du second champ sur cette ligne, qui correspond à l'horodatage et au pad a ;
après ça. /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. 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
ID:1234
, divisé en tableau c et utilise la valeur c [2] comme index dans le tableau a. arrays of arrays
, vous pouvez imprimer deux valeurs a[i]["TAG_START"]
et a[i]["TAG_END"]
directement 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