J'ai un file comme celui-ci:
A 100 A 200 A 300 #sum=600 B 400 B 500 #sum=900 A 600 A 700 A 800 #sum=2100
J'aimerais que la sortie soit:
A 600 B 900 A 2100 C sum_of_C D sum_of_D
Je peux le faire avec for
, sed
, grep
et awk
.
Cependant, comme j'apprends awk
, j'aimerais écrire un script awk
. Jusqu'à présent, j'ai:
if (${NR {print $1}} == ${NR-1 {print $1}}) sum+=$2 print $0"\t"sum else sum=$2 print $0"\t"sum
awk -f awkscript file
n'a pas abouti. Quelle est la solution?
Je ne suis pas complètement sûr de ce que vous essayez d'y faire. NR
est le nombre d'loggings; utilisez NF
pour le nombre de champs, si c'est ce que vous visez. Vous ne pouvez pas mettre {}
blocs au milieu de choses comme ça.
Je pense que ce que vous visez est de comparer la valeur d'un champ de cette ligne avec un champ de la ligne précédente, en imprimant la sum lorsque nous atteignons un nouveau «groupe» de données. Si c'est le cas, ce script fera ce que vous voulez et je pense que cela correspond à peu près à ce que vous visiez:
{ if (last && $1 != last) { print last, sum sum = 0 } sum = sum + $2 last = $1 } END { print last, sum }
Nous faisons une last
variable pour tenir la valeur du premier champ ( $1
) sur la ligne précédente. Nous l'utiliserons pour déterminer quel groupe nous examinons.
{ ... }
au niveau supérieur), nous vérifions d'abord si a) last
est défini (parce que nous ne voulons rien imprimer sur la première ligne) et b) la valeur de le premier champ est différent de la last
. Si c'est le cas, nous imprimons la valeur de last
, un espace (à cause de ,
) et la sum
nous avons calculée. (Si vous voulez un onglet, utilisez "\t"
entre guillemets comme vous aviez) sum
à zéro. $2
) à la sum
. last
afin que nous puissions l'utiliser pour la comparaison sur la ligne suivante. END { ... }
. Il fonctionne à la fin du programme lorsque nous manquons de données. Nous imprimons la sum et le groupe avec lequel nous travaillons comme nous l'avons fait auparavant. Si je cours:
awk -f sum.awk < data
avec votre file de données, j'obtiens cette sortie:
A 600 B 900 A 2100
comme voulu.
Il existe des façons plus simples de le faire, à la fois dans awk et autrement. En particulier, nous pouvons replace le corps ci-dessus par:
last && $1 != last { print last, sum sum = 0 } { sum = sum + $2 last = $1 }
Nous utilisons ici la syntaxe des blocs conditionnels de awk plutôt qu'un test explicite if
: le comportement de ce programme est identique à celui ci-dessus, mais il est plus idiomatique. Ce n'est pas très différent dans cet exemple, mais il est utile de savoir si vous apprenez awk.
Si l'exemple de file que vous avez donné est littéralement ce qu'il est, avec #sum=
lignes (ou similaire), vous pouvez utiliser ce script:
{ sum = sum + $2 if (NF == 3) { print $1, sum sum = 0 } }
Pour chaque ligne, ceci ajoute la valeur du second champ à la variable sum
. Sur les lignes qui ont exactement trois champs ( NF == 3
), nous imprimons notre total et réinitialisons la sum
à zéro.
Si votre file est suffisamment petit pour que toutes les sums puissent tenir en memory, vous pouvez faire quelque chose aussi simple que:
$ awk '{sum[$1]+=$2}END{for(pat in sum){print pat,sum[pat]}}' file A 2700 B 900
Voici la même chose qu'un script awk
commenté:
#!/usr/bin/awk -f { ## Here, we use $1 as the key of an associative array ## and increment its current value by $2. The result of ## this will be an array element for each different $1 in ## the file whose value will be the sum of all associated $2s. sum[$1]+=$2 } ## The END{} block is exacuted after the entire file ## has been processed. END{ ## Iterate through the keys of the array (the $1s), ## saving each as 'pat'. Then, print the current value of ## 'pat' as well as the associated value (the sum) from ## the array. for(pat in sum){ print pat,sum[pat] } }
Le seul problème possible avec cette approche est que si vous avez autant de lignes que le fait de conserver le tableau de $1
vous fera manquer de memory. Ce n'est pas très probable sur un système moderne. D'autre part, cette approche fonctionne également lorsque les lignes de votre file ne sont pas en ordre car il peut traiter des files non sortingés.