batch_warrior a écrit :
Bonsoir,
je génère automatiquement un fichier trié en fonction du premier champ qui ressemble à:
a;3
a;2.078
a;3
b;65
b;0.54
Il y a énormement de "1er champ" différent et le fichier comprend + de 100000 entrées. Je voudrais faire la somme des chiffres correspondant au même 1er champ et afficher le résultat sous la forme:
a 8.078
b 65.54
j'ai tenté des cat | cut ou encore cat | awk -F";" 'sum+=$2' mais ce n'est pas concluant.
est-ce que vous avez des pistes pour résoudre ce problème?
toutes les idées seront les bienvenues
|
Pas de pb... à condition expresse que le fichier soit vraiment trié (bon, sinon c'est pas bien grave mais faut le trier dans le script)
#!/bin/sh
# On crée un fichier de travail copie exacte du fichier d'origine
dir_tmp="$HOME/tmp"
name_tmp="`basename $0 .sh`_work.$$"
file_tmp="$dir_tmp/$name_tmp"
cp fichier "$file_tmp" # Ici, on peut le trier s'il ne l'est pas déjà
# On rajoute un élément final qu fichier de travail
echo "_EOF_" >>"$file_tmp"
# On stocke le fichier de travail dans le canal 3
exec 3<"$file_tmp"
# On traite le canal 3 ligne par ligne
while read lig 0<&3
do
# On découpe la ligne en "id + valeur"
id=`echo $ligne |cut -f1 -d\;`
valeur=`echo $ligne |cut -f2 -d\;`
# Si l'id est différent du précédent (on a changé d'id)
if test "$id" != "$mem_id"
then
# Si le précédent id existe (on n'est pas sur le premier)
if test -n "$mem_id"
then
# On affiche l'id + compteur
echo "$mem_id $cpt"
fi
# On mémorise l'id pour le nouveau bloc - RAZ des compteurs
mem_id=$id
cpt=0
fi
# on incrémente le compteur avec la valeur
cpt=`expr $cpt + $valeur`
done
# On efface le fichier temporaire
rm -f "$file_tmp" |
Principe de ce script
Je vais traiter le fichier ligne par ligne. A chaque ligne, je vérifie si l'id a changé par rapport à la ligne d'avant. Si c'est le cas et s'il y avait un id sur la ligne d'avant, c'est qu'on n'est pas sur la première ligne donc qu'il y a un compteur à afficher pour le bloc précédent. De toute façon, comme on est sur un nouveau bloc, je mémorise mon identifiant et réinitialise mon compteur.
Ensuite, à chaque lieng, j'ajoute la valeur au total déjà accumulé.
La chaine "EOF" ajoutée à la fin du fichier sert pour pouvoir afficher le dernier bloc accumulé (puisque, quand le script rencontrera "EOF", il aura forcément "id" différent de "mem_id" ). Sans ce "EOF", je ne pourrais pas afficher le dernier total. Et comme je rajoute un élément au fichier d'origine, je suis obligé d'utliser un fichier de travail temporaire pour ne pas polluer le fichier d'origine.
Ce fichier temporaire doit impérativement être unique (on sait jamais, deux utilisateurs différents ou même moi je pourrais lancer ce script plusieurs fois en parallèle et il ne faut pas qu'il y ait collision dans le fichier temporaire) donc j'inclus dans son nom la variable "$$" identifiant mon pid. J'y inclus aussi le nom du script comme ça, ce fichier est facile à retrouver et à nettoyer si le script s'interromp brutalement avant la fin.
la syntaxe "exec 3<fic" permet de créer un nouveau canal d'entrée qui contient l'intégralité du fichier. Ensuite, chaque "while read ... 0<&3" va y lire une ligne à la fois et sortira lorsque tout aura été lu.
J'espère avoir été clair. Mais c'est dommage de le faire en shell pour un truc de 100000 entrées (long long long, au-moins 20 minutes) alors que Python pourrait te traiter ça en 3 secondes. Le pb, c'est que je ne connais pas assez bien Python (j'ai commencé il y a à peine 3 jours) pour te fournir la solution. Ptet tu pourrais aller poster ton truc dans le forum Python...
Message édité par Sve@r le 22-06-2006 à 23:40:28
---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.