Traiter plusieurs fichiers avec awk

Je dois traiter beaucoup de fichiers txt (16 millions de lignes pour chaque fichier) en utilisant awk. Je dois lire par exemple dix fichiers:

Fichier n ° 1:

en sample_1 200 en.n sample_2 10 en sample_3 10 

Fichier n ° 2:

 en sample_1 10 en sample_3 67 

Fichier n ° 3:

 en sample_1 1 en.n sample_2 10 en sample_4 20 

Je voudrais avoir un résultat comme celui-ci:

titre source f1 f2 sum f3 (f1, f2, f3)

 en sample_1 200 10 1 211 en.n sample_2 10 0 10 20 en sample_3 10 67 0 77 en sample_4 0 0 20 20 

Voici ma première version du code:

 #! /bin/bash clear #var declaration BASEPATH= YEAR="2014" RES_FOLDER="processed" FINAL_RES="2014_06_01" #results folder creation mkdir $RES_FOLDER #processing awk 'NF>0{a[$1" "$2]=a[$1" "$2]" "$3}END{for(i in a){print ia[i]}}' $BASEPATH/$YEAR/* > $RES_FOLDER/$FINAL_RES 

Et voici ma sortie:

 en sample_1 200 10 1 en.n sample_2 10 10 en sample_3 10 67 en sample_4 20 

Je suis un peu confus quant à la manière de mettre une colonne zéro où aucune occurrence n’est trouvée et comment obtenir la sum de toutes les valeurs. Je sais que je dois utiliser ceci:

 {tot[$1" "$2]+=$3} END{for (key in tot) print key, tot[key]} 

J’espère que quelqu’un aidera. Je vous remercie.

******** EDITED ********

J’essaie d’atteindre mon résultat d’une manière différente. Je crée un script bash comme celui-ci, il produit un fichier sortingé avec toutes mes clés, il est très volumineux, environ 62 millions d’enregistrements, je divise ce fichier en morceaux et je passe chaque morceau à mon script awk.

FRAPPER:

 #! /bin/bash clear FILENAME= BASEPATH= mkdir processed/slice cat $BASEPATH/dataset/* | cut -d' ' -f1,2 > $BASEPATH/processed/aggr sort -u -k2 $BASEPATH/processed/aggr > $BASEPATH/processed/sorted split -d -l 1000000 processed/sorted processed/slice/slice- echo $(date "+START PROCESSING DATE: %d/%m/%y - TIME: %H:%M:%S") for filename in processed/slice/*; do awk -v filename="$filename" -f algorithm.awk dataset/* >> processed/$FILENAME done echo $(date "+END PROCESSING DATE: %d/%m/%y - TIME: %H:%M:%S") rm $BASEPATH/processed/aggr rm $BASEPATH/processed/sorted rm -rf $BASEPATH/processed/slice 

AWK:

 BEGIN{ while(getline < filename){ key=$1" "$2; sources[key]; for(i=1;i<11;i++){ keys[key"-"i] = "0"; } } close(filename); } { if(FNR==1){ ARGIND++; } key=$1" "$2; keys[key"-"ARGIND] = $3 }END{ for (s in sources) { sum = 0 printf "%s", s for (j=1;j<11;j++) { printf "%s%s", OFS, keys[s"-"j] sum += keys[s"-"j] } print " "sum } } 

Avec awk, je pré-alloue mon tableau final et, en lisant le dossier dataset/* , je remplis son contenu. J’ai compris que mon goulot d’étranglement provenait de l’itération sur awk du dossier de jeux de données (10 fichiers de 16 000 000 lignes chacun). Tout fonctionne sur un petit dataset, mais avec des données réelles, la RAM (30 Go) est saturée. Quelqu’un at-il des suggestions ou des conseils? Je vous remercie.

 $ cat tst.awk { key = $1" "$2 keys[key] val[key,ARGIND] = $3 } END { for (key in keys) { sum = 0 printf "%s", key for (fileNr=1;fileNr<=ARGIND;fileNr++) { printf "%s%s", OFS, val[key,fileNr]+0 sum += val[key,fileNr] } print sum } } $ awk -f tst.awk file1 file2 file3 en sample_4 0 0 2020 en.n sample_2 10 0 1020 en sample_1 200 10 1211 en sample_3 10 67 077 

Ce qui précède utilise GNU awk pour ARGIND, avec d'autres awks il suffit d'append une ligne FNR==1{ARGIND++} au début. Pipe la sortie à sort si nécessaire.

 awk -vn=" " 'function w(m,p){while(split(a[m],t)!=b+2)sub(p," 0&",a[m])}FNR<2{f=FILENAME;o=o?o" <"f">":"<"f">";q=q?q","f:f;++b}{a[$1" "$2]=a[$1" "$2]?a[$1" "$2]" "$NF:$0;w($1" "$2," [^ ]*$");c[$1" "$2]+=$NF}END{print n,o,"sum<("q")>";for(i in a){w(i,"$");print a[i],c[i]|"sort -k2"}}' * <source />  <f1> <f2> <f3> sum<(f1,f2,f3)> en sample_1 200 10 1 211 en.n sample_2 10 0 10 20 en sample_3 10 67 0 77 en sample_4 0 0 20 20</f3></f2></f1> 

Étant donné que vos fichiers sont assez volumineux, vous souhaiterez peut-être utiliser la fonction join – cela pourrait être plus rapide et / ou utiliser moins de mémoire. Cependant, les fichiers doivent être sortingés et avoir un seul champ de jointure.

 join -a1 -a2 -e0 -o0,1.2,2.2 <(sed $'s/ /\034/' file1 | sort) \ <(sed $'s/ /\034/' file2 | sort) | join -a1 -a2 -e0 -o0,1.2,1.3,2.2 - \ <(sed $'s/ /\034/' file3 | sort) | awk '{sub(/\034/," "); print $0, $3+$4+$5}' 

Explication fournie sur demande