Make a countdown column with awk
Hello everyone,
A little question about awk. Given a column of numbers in a "list" file. 0 1 2 3 4 5 I wish to print a list displaying (sum - value) at each row, as a countdown of the sum of the first list. 15 13 10 6 1 0 I have tried to set up a script to do it, but it does not work. Code:
cat list | awk ' BEGIN {S+=$1} END {a=$1} {print (S-$a)}' |
BEGIN block is only entered once - likewise the END block; one right at the start, one right at the finish. You don't need the BEGIN. There are other logic errors, but that is for you to solve.
|
The action at the END is meaningless, since there is no $1 at the END and you do nothing with variable "a".
Similarly, there is no $1 at the BEGINning. The print action is executed at each input line, but there is no such thing as $a in awk. I suppose you get syntax errors and the program doesn't even start running. Now, I don't really understand how to correlate the input and output, otherwise I could give you hints how to construct the awk program. 15 is obviously the sum of all numbers, but how do you compute 13? I would have expected 14 there (sum is 15, value is 1, sum-value is 14). EDIT: In any case, you need two passes over the file, since you need to know the sum of all numbers when processing the first line. |
Oh, and you commit cat abuse, too.
|
Quote:
15 10 6 3 1 0 Pseudo code: Get the sum of all lines...print it Read through the list in reverse order Subtract the amount on each line from the total and print the result. Homework?? |
Thank you for your comments.
There is indeed a mistake in my example. The input: 0 1 2 3 4 5 The output: 15 14 12 9 5 0 It makes more sense now! syg00 here is my explanation of the use of BEGIN and END. For the formula: I produce a sum of the columns with {S+=$1}. I add BEGIN for this operation would be done before the others. I then record the value of column 1 in the variable a. It is quoted $a afterward to indicate it is a variable. Finally, I print the subtraction sum_of_the_column - $a. I have added END for these operations to be done after the sum. scasey what you describe is exactly why I have tried to do. I am aware that if no one recognize the formula it is very far from the solution. I will search for another way to design this script. |
Quote:
awk is something like a for loop (like sed/grep and other "similar" apps). I mean they take one line at once, and process the input line by line. So the awk (grep/sed/...) script will be executed on every and each line. BEGIN and END are two keywords. BEGIN and END blocks will be executed only once, before the first line an after the last. (ok, there is an exception where we have more than one input files, but you can ignore it right now). From the other hand the 15 is the total sum of those numbers, you need to process all the lines to get it. To get the result list you need to process all the lines once more. This is the second half: Code:
awk -vX=15 '{X-=$1; print X}' a.list |
Quote:
|
Much thanks to your answer pan64.
Here is a script that I have set up based on yours. I was unaware that variables ($a here) were not usable in the awk command, and that -v was used to input variables. That was what berndbausch was referring to with $a, without quoting it literally. Quote:
I tend to forget a lot. I will find tutorials and will try to train on awk daily, else I will forget again. Thanks for your help. |
glad to help you.
If you really want to say thanks just click on yes. |
Now that you seem to have a process that works for you.
May I suggest you look at your awk manual you are using, or this one if you do not, and try placing the numbers in an array whilst summing your total and then you can simply use your END block to unpack the array in the order you like and subtract at will |
To achieve the required output, it is easier to store the running totals (from s+=$1) into an array (with t[NR]=s), then use the END block to subtract each running total from the final sum (with {(for i in t) {print s-t[i]}} ).
|
@allend. Even saving the running totals is not needed. I believe what grail implied above was
Code:
awk '{s+=$1;_[$1]}END{for(i in _)print i,s-i}' |
You can let awk read the input twice. At the first pass (where NR equals FNR) sum up and jump to the next input cycle (skipping the following code).
At the second pass run the following code. Code:
awk 'NR==FNR { X+=$1; next } { X-=$1; print X }' list list |
All times are GMT -5. The time now is 09:53 PM. |