Сумма альтернативных значений в столбце с использованием команды sed или nawk

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 

Желаемый 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 

Строка 11 представляет собой сумму альтернативных строк, начиная с строки 1 в 1-м, 2-м и 3-м столбцах, строка 12 представляет собой сумму альтернативных строк, начиная с строки 2 в 1-м, 2-м и 3-м столбцах. Строка 13 представляет собой сумму столбцов в строке 11 и строке 12. Я использую KSH и Solaris 5.10. Значения во входном файле могут быть не последовательными и не должны превышать трехзначных целых чисел. Мой входной файл будет содержать только 10 строк. Как достичь этого?

 $ 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 

Выше было протестировано на GNU awk и linux.

Как это работает

  • -v OFS='\t'

    Необязательно: это устанавливает вывод в разделитель.

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

    Это проходит через каждый столбец, добавляя его значения к массиву s . Для каждого столбца i четные номера добавляются к s[2,i] а строки с нечетными номерами добавляются к s[1,i] . Колонка i во всех строках добавляется к s[3,i] .

    Затем печатается эта строка.

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

    После того, как мы достигли конца файла, результаты печатаются сначала для строк с нечетным номером ( n=1 ), затем четных строк ( n=2 ), затем итоговых ( n=3 ).

ВС / Solaris

У меня было несколько отчетов о том, что awk по умолчанию у Sun / Solaris имеет проблемы. Пожалуйста попробуйте:

 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 

Или:

 /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 

Или:

 /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 

Вы почти наверняка хотите использовать awk для этого, а не sed . Вот скрипт awk который может это сделать:

 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 

Это использует встроенную переменную записи «NR», способ которой awk разбивает текстовые файлы на поля и конструкцию «END».

Ну, я сам нашел очень основное решение этой проблемы. Но хотелось бы, чтобы кто-то дал лучший ответ.

 #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` 

Я знаю, что мое решение очень простое. Но я старался изо всех сил.

 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 - 

Это должно сработать для вас. Он работает путем обработки некоторой макропроцессорной обработки для dc калькулятора / компилятора w / sed in-stream.

В основном, sed сообщает dc (компилятор bc – вы можете использовать его в системе Solaris), чтобы отслеживать 6 значений, загружать их один раз в каждую другую строку ввода, увеличивать их на столбец и еще раз сохранять результаты. В последней строке ввода sed сообщает dc чтобы снова вызвать их обратно и распечатать все 6 значений в stdout. Чтобы получить итоговые значения для строки 13, мы должны снова вызвать наши накопленные итоговые значения и добавить их:

 l3l6+...f 

Мы выгружаем dc stderr в /dev/null потому что в первой строке, когда он пытается использовать любые значения из любого из массивов [123456] массив все равно будет пустым, и он выдаст предупреждения об этом. Это не имеет никакого значения, потому что в остальное время они не будут пустыми, и мы сохраним / восстановим их при необходимости.

Наконец, другой sed объединяет все это – он добавляет вывод dc в хвост file и заменяет все пробелы с одной вкладкой для каждой строки (для которой я использовал \t escape здесь, но который, вероятно, должен быть буквально <tab> в реальном скрипте) .

ВЫВОД

 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