3

foo.txt:

1  10     11
2   20     22
3   30     32
4   40     42
5   50     52
6   60     62
7   70     72
8   80     82
9   90     92
10  100   110

Desired Out.txt :

1  10     11
2   20     22
3   30     32
4   40     42
5   50     52
6   60     62
7   70     72
8   80     82
9   90     92
10  100   110
25  250   275   #Line 11
30  300   330   #Line 12
45  550   595  #Line 13

Line 11 is a sum of alternate lines starting from line 1 in 1st,2nd and 3rd columns, Line 12 is sum of alternate lines starting from line 2 in 1st, 2nd and 3rd columns. Line 13 is sum of columns in line 11 and line 12. I am using KSH and Solaris 5.10, The values in the input file may not be sequential and will not be more than 3 digit integers. My input file will only have 10 lines. How to achieve this?

4 Answers 4

3
$ awk -v OFS='\t' '{for (i=1;i<=NF;i++) {s[2-NR%2,i]+=$i;s[3,i]+=$i;};$1=$1;print} END{for (n=1;n<=3;n++) print s[n,1],s[n,2],s[n,3]}' foo.txt 
1       10      11
2       20      22
3       30      32
4       40      42
5       50      52
6       60      62
7       70      72
8       80      82
9       90      92
10      100     110
25      250     259
30      300     318
55      550     577

The above was tested on GNU awk and linux.

How it works

  • -v OFS='\t'

    Optional: this sets the output to tab-separated.

  • {for (i=1;i<=NF;i++) {s[2-NR%2,i]+=$i;s[3,i]+=$i;}; $1=$1; print}

    This loops through each column, adding its values to the array s. For each column i, even numbered rows are added to s[2,i] while odd-numbered rows are added to s[1,i]. Column i on all rows is added to s[3,i].

    This row is then printed.

  • END{for (n=1;n<=3;n++) print s[n,1],s[n,2],s[n,3]}

    After we have reached the end of the file, the results are printed, first for the odd-numbered lines (n=1), then the even-numbered lines (n=2), then the total (n=3).

Sun/Solaris

I have had multiple reports that the default awk on Sun/Solaris has issues. Please try:

nawk -v OFS='\t' '{for (i=1;i<=NF;i++) {s[2-NR%2,i]+=$i;s[3,i]+=$i;};$1=$1;print} END{for (n=1;n<=3;n++) print s[n,1],s[n,2],s[n,3]}' foo.txt 

Or:

/usr/xpg4/bin/awk -v OFS='\t' '{for (i=1;i<=NF;i++) {s[2-NR%2,i]+=$i;s[3,i]+=$i;};$1=$1;print} END{for (n=1;n<=3;n++) print s[n,1],s[n,2],s[n,3]}' foo.txt 

Or:

/usr/xpg6/bin/awk -v OFS='\t' '{for (i=1;i<=NF;i++) {s[2-NR%2,i]+=$i;s[3,i]+=$i;};$1=$1;print} END{for (n=1;n<=3;n++) print s[n,1],s[n,2],s[n,3]}' foo.txt 
3

You almost certainly want to use awk for this one, not sed. Here's an awk script that can do it:

awk '
    (NR%2) == 1 {
        odd_col_1 += $1;
        odd_col_2 += $2;
        odd_col_3 += $3;
        print $0;
    }
    (NR%2) == 0 {
        even_col_1 += $1;
        even_col_2 += $2;
        even_col_3 += $3;
        print $0;
    }
    END {
        print odd_col_1, odd_col_2, odd_col_3;
        print even_col_1, even_col_2, even_col_3;
        print odd_col_1+even_col_1, odd_col_2+even_col_2, odd_col_3 + even_col_3;
    }
' foo.txt

This takes advantage of the "NR" record number built-in variable, the way awk breaks up text files into fields, and the "END" construct.

1

Well, I found a very basic solution for this problem myself. But would like someone to provide a better answer.

#remove even lines
sed -i '0~2d' foo.txt > oddlines

#oddlines sum
awk '{a=a+$1}{b=b+$2}{c=c+$3}END{print a,b,c}' oddlines > oddlines_sum

#remove even lines
sed -i '1~2d' foo.txt > evenlines

#evenlines sum
awk '{a=a+$1}{b=b+$2}{c=c+$3}END{print a,b,c}' evenlines > evenlines_sum

#combine 
cat evenlines_sum >> oddlines_sum

#for total sum of foo.txt
awk '{a=a+$1}{b=b+$2}{c=c+$3}END{print a,b,c}' foo.txt > foo_sum

#final output
cat oddlines_sum >> foo.txt
cat foo_sum >> foo.txt`

I know my solution is very basic. But I tried my best.

1
  • Please have a look at the formatting tools available. Commented Jun 20, 2015 at 8:42
1
sed '   1x;1s/^/654321/;1x;N;y/ /\n/;G;:t
        s/\([0-9]*\)\n*\n\(.*\)\(.\)/l\3\1+s\3 \2/;tt
        p;$!d;g;s/./l&/g;s/$/fcl3l6+l2l5+l1l4+f/' file |
dc 2>/dev/null |sed '11,$N;/\n/N;s/[^0-9] */\t/g' file -

This should work for you. It works by handling some macro preprocessing for the dc calculator/compiler w/ sed in-stream.

Basically, sed tells dc (bc's compiler - you sould have it on a Solaris system) to keep track of 6 values, to load them once every other input line, increment them per column, and to once again save the results. On the last input line, sed tells dc to call them back up again and print all 6 values to stdout. To get the totals for line 13 all we have to do is call up our stored totals again and add them up:

l3l6+...f

We dump dc's stderr to /dev/null because on the very first line when it tries to load any values from any of the [123456] arrays the array will yet be empty, and it will issue warnings to that effect. This is not of any significance because for the rest of the time they will not be empty, and we will save/restore them when necessary.

Last, another sed sticks the whole thing together - it appends dc's output to the tail of file and replaces all spaces w/ a single tab for each line (for which I used the \t escape here, but which should probably be a literal <tab> character in the actual script).

OUTPUT

1       10      11
2       20      22
3       30      32
4       40      42
5       50      52
6       60      62
7       70      72
8       80      82
9       90      92
10      100     110
25      250     259
30      300     318
55      550     577

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.