2

I have this input file:

Adrian 20.6
Bruce 40.2
Adrian 30.4
Janick 24.2
Dave 42.7
Bruce 42.9
Janick 41.2

And with a answer I've got here, I was able to sum the numbers, but now the numbers are float and I don't know how to print them.

For now, my output is:

Adrian 50
Bruce 82
Dave 42
Janick 65

But it should be with the decimals as well.

$(awk '{sum[$1] += $2} END {for (i in sum) print i, sum[i]}' $arq > temp.txt && sort temp.txt)

I've made that code to sort by alphabetical order.

Lvcrft
  • 57
  • 5
  • 1
    With `printf` just like C (without the `(..)`). See [man 3 printf](https://man7.org/linux/man-pages/man3/printf.3.html) Why are you calling `awk` within a *Command Substitution*, e.g. `$(awk ... )` -- that would tend to show you are calling `awk` on a per-iteration basis. That is generally the least efficient way to use any shell utility. Sometimes needed, but often not. That can work to initialize an array, but there isn't enough information provided to tell. – David C. Rankin Jun 01 '21 at 00:28
  • Because it's inside a script. And I've tried with printf, but it didn't work. Or I don't know where to insert the printf. – Lvcrft Jun 01 '21 at 00:33
  • 1
    `awk` handles floating-point values fine. Your summation of `sum[$1] += $2` will produce a floating point number. Simply using `awk '{sum[$1] += $2} END {for (i in sum) print i, sum[i]}' file` produces, e.g. `Bruce 83.1` and `Dave 42.7`, ... Adding `| sort` outputs the results in sorted order. Adding `| sort > temp.txt` redirects the sorted output with floating point numbers to `temp.txt`, no *Command Substitution* needed (you may be closer than you think `:)` – David C. Rankin Jun 01 '21 at 00:37
  • I've tried removing the sort, and the command substitution. It didn't work as well. Don't know what's happening, but still trying. – Lvcrft Jun 01 '21 at 00:49

2 Answers2

6

You're in a locale where the decimal separator is not . so the . is being treated as any other character that's not part of a number (e.g. a or @) and so your numbers are being truncated to the part before the ..

Do LC_ALL=C awk '...' to set your locale to one that does use a . for the decimal separator and then your script will work as-is.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185
2

I think you are closer than you think, but your problems stem from not handling putting the pieces together in the right order. By default awk handles floating-point math. Your summation of sum[$1] += $2 will be done with floating point numbers if your input numbers contains decimal places.

Why you are using a Command Substitution, e.g. $(..) is unclear. That isn't needed to fill temp.txt with the sorted sums from your input. Let's take for example your input file in a file named input. Then to sum and output the values you need nothing more than:

awk '{sum[$1] += $2} END {for (i in sum) print i, sum[i]}' input

Which results in the output of:

Bruce 83.1
Dave 42.7
Adrian 51
Janick 65.4

To sort the results, simply pipe the awk output to sort, e.g.

awk '{sum[$1] += $2} END {for (i in sum) print i, sum[i]}' input | sort

Result:

Adrian 51
Bruce 83.1
Dave 42.7
Janick 65.4

Now, to fill temp.txt, you simply need to redirect the output to temp.txt, e.g.

awk '{sum[$1] += $2} END {for (i in sum) print i, sum[i]}' input | sort > temp.txt

temp.txt now contains the sorted output. To nicely tabularize the output, you can format the output by saving the max length for $1 and setting the name field-width to max and then outputting the floating-point number in a fixed format (total floating-point field-width 10 with 1 digit following the decimal point), e.g.

{sum[$1] += $2; len=length($1); if(len>max)max=len} ...  printf "%-*s%10.1f\n", max, i, sum[i]

Or in full-form:

awk '{sum[$1] += $2; len=length($1); if(len>max)max=len} END {for (i in sum) printf "%-*s%10.1f\n", max, i, sum[i]}' input | sort > temp.txt

With the results in temp.txt now being:

$ cat temp.txt
Adrian      51.0
Bruce       83.1
Dave        42.7
Janick      65.4
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85