Je vous recommande fortement d'imprimer tous les domaines possibles pour chaque ligne et de fournir des valeurs « N/A », le cas échéant car il va rendre vos données beaucoup plus facile de faire du traitement plus loin:
$ cat tst.awk
BEGIN { OFS="," }
{
delete name2val
numFlds = split($0,flds,/[=:]|[[:space:]]+/,seps)
for (fldNr=1;fldNr<numFlds;fldNr+=2) {
name = flds[fldNr]
if (!seen[name]++) {
names[++numNames] = name
}
name2sep[name] = seps[fldNr]
name2val[name] = flds[fldNr+1]
}
}
NR!=FNR {
for (nameNr=1;nameNr<=numNames;nameNr++) {
name = names[nameNr]
sep = name2sep[name]
val = (name in name2val ? name2val[name] : "N/A")
printf "%s%s%s%s", name, sep, val, (nameNr<numNames ? OFS: ORS)
}
}
$ awk -f tst.awk file file
ID=1234,PCharge=2,ext=5,IMSI=1234,Int:123,Charge=3
ID=1234,PCharge=2,ext=5,IMSI=1234,Int:N/A,Charge=3
ID=1234,PCharge=2,ext=5,IMSI=1234,Int:4567,Charge=3
ID=1234,PCharge=2,ext=5,IMSI=1234,Int:N/A,Charge=3
ID=1234,PCharge=2,ext=5,IMSI=1234,Int:N/A,Charge=3
Les utilisations ci-dessus GNU awk pour le 4ème arg à scinder(). Vous avez seulement besoin de cela parce que vous utilisez :
dans Int:value
alors que toute autre paire nom-valeur utilise =
comme dans Charge=value
. Si vous étiez satisfait de Int=value
ou de tout autre séparateur cohérent dans la sortie, vous n'auriez pas besoin de sauvegarder le séparateur et n'auriez donc pas besoin de GNU awk pour le 4ème arg à split(). Notez que ce qui précède ne nécessite pas de codage en dur des noms de champs, il utilise simplement les noms de votre fichier d'entrée en utilisant une approche en deux passes pour lire tous les noms de chaque ligne sur le premier passage de sorte qu'il sait ce que tous les noms de champs possibles sont pour imprimer dans chaque ligne sur la deuxième passe.
Vous devriez également envisager de modifier le format de sortie pour être sous forme de tableau afin que vous puissiez travailler avec elle dans Excel, par exemple:
$ cat tst.awk
BEGIN { FS="[=:]|[[:space:]]+"; OFS="," }
{
delete name2val
for (fldNr=1;fldNr<NF;fldNr+=2) {
name = $fldNr
if (!seen[name]++) {
names[++numNames] = name
}
name2val[name] = $(fldNr+1)
}
}
NR!=FNR {
if (FNR==1) {
for (nameNr=1;nameNr<=numNames;nameNr++) {
name = names[nameNr]
printf "%s%s", name, (nameNr<numNames ? OFS: ORS)
}
}
for (nameNr=1;nameNr<=numNames;nameNr++) {
name = names[nameNr]
val = (name in name2val ? name2val[name] : "N/A")
printf "%s%s", val, (nameNr<numNames ? OFS: ORS)
}
}
$ awk -f tst.awk file file
ID,PCharge,ext,IMSI,Int,Charge
1234,2,5,1234,123,3
1234,2,5,1234,N/A,3
1234,2,5,1234,4567,3
1234,2,5,1234,N/A,3
1234,2,5,1234,N/A,3
Notez que ce second script ne nécessite pas GNU awk, il va travailler dans N'importe quel awk POSIX, car il n'a pas besoin de sauvegarder la chaîne séparatrice en utilisant le 4ème arg spécifique à gawk à split().
merci, son sonne bien mais ce n'est pas de travail pour ligne comme: "ID = 1234 PCharge = 2 ext = 5 IMSI = 1234 Int: 4567 Charge = 3" –
@ Jafar.A J'ai mis à jour la réponse. Les champs optionnels seront imprimés sur la ligne à laquelle ils appartiennent. Dans votre exemple dans la question, les lignes avec des champs optionnels apparaissent à la fin. Y a-t-il un tri des rangées impliquées? Si oui, veuillez préciser les détails dans la question. –
@ Lars Fischer J'ai essayé votre script mais j'ai une erreur de syntaxe: 'awk: erreur de syntaxe à la ligne source 11 contexte est split ($ f, kv, >>>" [=:] ", <<< Est-ce que la fonction split prend vraiment 4 arguments? – user2243670