2017-01-14 2 views
-1

J'ai un fichier commecolonnes de tri d'un fichier à l'aide des outils Linux

ID=1234 PCharge=2 ext=5 IMSI=1234 Int:123 Charge=3 
ID=1234 PCharge=2 ext=5 IMSI=1234 Charge=3 
ID=1234 PCharge=2 ext=5 IMSI=1234 Int:4567 Charge=3 
Charge=3 ID=1234 PCharge=2 ext=5 IMSI=1234 
PCharge=2 ID=1234 Charge=3 ext=5 IMSI=1234 

Comment puis-je trier ce fichier à quelque chose comme ça?

ID=1234 Charge=3 PCharge=2 ext=5 IMSI=1234 
ID=1234 Charge=3 PCharge=2 ext=5 IMSI=1234 
ID=1234 Charge=3 PCharge=2 ext=5 IMSI=1234 
ID=1234 Charge=3 PCharge=2 ext=5 IMSI=1234 
ID=1234 Charge=3 PCharge=2 ext=5 IMSI=1234 Int:123 
ID=1234 Charge=3 PCharge=2 ext=5 IMSI=1234 Int:4567 

Répondre

3

Vous pouvez utiliser un script awk comme ceci:

script.awk (mise à jour pour les touches en option et séparateurs ":" et "=")

BEGIN { keys[1] = "ID" 
     keys[2] = "Charge" 
     keys[3] = "PCharge" 
     keys[4] = "ext" 
     keys[5] = "IMSI" 
     keys[6] = "Int" 
     } 

NF>0 { delete values # reset each line due to optional keys 
     for(f =1 ; f <= NF; f++) { 
      split($f, kv, "[=:]",seps) # split using RE separator and store individual separator in seps 
      values[ kv[1] ] = seps[1] kv[2] # prepend individual separator to value 
     } 

     tmp = "" 
     for(k = 1; k <= length(keys); k++) { 
      if(keys[k] in values) { # check due to optional keys 
       tmp=sprintf("%s%s%s%s", 
          tmp, 
          keys[k], values[keys[k]], 
          (k < NF) ? OFS : "") 
      } 
     } 
     print tmp 
     } 

Run si : awk -f script.awk yourfile. Le bloc configure la séquence de champ de sortie. La condition NF > 0 sur le second bloc ignore les lignes vides.

Le deuxième bloc effectue l'itération sur key=value champs (awk effectue la division aux espaces dans les champs) et stocke les paires clé/valeur. Dans la deuxième boucle, les paires stockées sont ajoutées à tmp pour la sortie dans la séquence précédemment définie.

+0

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" –

+0

@ 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. –

+0

@ 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

2

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