2016-12-07 3 views
3

У меня есть выход из сценария thescript, который гласит:ОСУЩЕСТВЛЕНИЮ `sumproduct` в UNIX оболочки

202 1 0 1 0 0 0 

Теперь я хочу, чтобы выборочно просуммировать число с awk, в зависимости от стоимости ${SUM_MASK}:

SUM_MASK=1,1,0,0,0,0,0 

Я хотел бы иметь что-то вроде:

thescript | awk <SOMETHING> 

где выход каждое число thescript умножается на соответствующее число в ${SUM_MASK}, получение:

203 

как результат: 203 = 202 * 1 + 1 * 1 + 0 * 0 + 1 * 0 + 0 * 0 + 0 * 0 + 0 * 0

Это было бы похоже на функцию sumproduct в программном обеспечении для работы с электронными таблицами.

Следующие snipets код сделать трюк, но я хотел бы избежать использования подстановки процессов:

SUM_MASK="1,1,0,0,0,0,0"; paste <(thescript) <(echo ${SUM_MASK} | tr ',' '\n') | awk '{ SUM += $1 * $2 } END { print SUM }' 

и именованные каналы:

SUM_MASK="1,1,0,0,0,0,0"; mkfifo fA; mkfifo fB; thescript > fA & echo ${SUM_MASK} | tr ',' '\n' > fB & paste fA fB | awk '{ SUM += $1 * $2 } END { print SUM }' > result.text; rm -f fA fB 

, как я мог бы достичь этого?

+0

Зачем вам 'awk' для этого? –

+0

Я предполагаю, что число элементов не всегда фиксировано в 7? –

+0

BTW, я бы подумал взять оба входа из командной строки вашей функции или скрипта и объединить формат (чтобы разрешить либо пробелы *, либо * запятые для обоих, например). Для читателя немного неочевидно, почему был выбран выбранный вами вызов (с одним аргументом, взятым из предопределенной переменной, а другой из stdin). –

ответ

3
echo "202 1 0 1 0 0 0" | 
awk -v summask="1,1,0,0,0,0,0" ' 
    BEGIN {split(summask, mask, /,/)} 
    { sumproduct=0 
     for (i=1; i<=NF; i++) { 
      sumproduct += $i * mask[i] 
     } 
     print sumproduct 
    } 
' 
203 
+0

Это определенно один из лучших ответов на вопрос. – norok2

3

Здесь нет необходимости в внешних инструментах, таких как awk - bash способен разрешать это только с помощью встроенных возможностей. Рассмотрим ниже реализацию в виде функции:

sumproduct() { 
    local -a sum_inputs sum_mask 
    local idx result 

    # read your sum_inputs into an array from stdin 
    IFS=', ' read -r -a sum_inputs # this could be <<<"$1" to use the first argument 

    # and your sum_mask from the like-named variable 
    IFS=', ' read -r -a sum_mask <<<"$SUM_MASK" # or <<<"$2" for the second argument 

    # ...iterate over array elements in sum_inputs; find the corresponding sum_mask; math. 
    result=0 
    for idx in "${!sum_inputs[@]}"; do 
    ((result += ${sum_mask[$idx]} * ${sum_inputs[$idx]})) 
    done 
    echo "$result" 
} 

Чтобы проверить это:

echo "202 1 0 1 0 0 0" | SUM_MASK=1,1,0,0,0,0,0 sumproduct 

... правильно дает:

203 
+1

Хорошее решение. Возможное незначительное улучшение заключается в том, чтобы отключить обновление переменной 'result':' ((result + = sum_mask [idx] * sum_inputs [idx])). – pjh

+0

Я не уверен, что это будет работать с версией bash, используемой службой, но я буду тестировать и сообщать об этом. Очень приятное решение! – norok2

+0

@ norok2, все здесь совместимо с bash 3.2 - выпущено в 2006 году, поэтому мы говорим о полном десятилетии. Я на самом деле довольно уверен (но не тестировал), что совместимость распространяется и на 2.x, что означает конец 90-х. –

3

Вам действительно не нужен сумма продукта, но суммирование маскируется, например, это должно быть быстрее, если у вас много скрытых столбцов.

$ awk -v mask='1,1,0,0,0,0,0' 'BEGIN {n=split(mask,m,","); 
             for(i=1; i<=n; i++) if(m[i]) ix[i]} 
            {sum=0; 
             for(i in ix) sum += $i; 
             print sum}' file 

203 
+0

Я не вижу, как этот ответ отличается от одного @glennjackman, опубликованного 2 часа назад. – bishop

+0

Во-первых, нет размножения. Во-вторых, размер основного цикла основан на ненулевых позициях, но не на полном размере маски. В противном случае существует очень много способов вычислить сумму. – karakfa

+0

А, действительно. Благодаря! Вот плюс. – bishop

0

С однозначных множителей можно сделать простой цикл

SUM_MASK=1,1,0,0,0,0,0 
offset=0 
sum=0; 
for i in 202 1 0 1 0 0 0; do 
    j="${SUM_MASK:$offset:1}" 
    ((sum += i * j)) 
    ((offset+=2)) 
done 
echo "${sum}" 

Этот раствор может быть использован в сценарии prodsum, который можно назвать с thescript | prodsum:

offset=0 
sum=0; 
for i ; do 
    j="${SUM_MASK:$offset:1}" 
    ((sum += i * j)) 
    ((offset+=2)) 
done 
echo "${sum}" 

EDIT : Когда SUB_MASK может иметь номера> 9, используйте следующее:

SUM_MASK=1,10,0,0,0,0,0 
sum=0; 
remaining_mask="$SUM_MASK" 

for i in 202 1 0 1 0 0 0; do 
    j="${remaining_mask%%,*}" 
    remaining_mask="${remaining_mask#*,}" 
    ((sum += i * j)) 
done 
echo "${sum}" 
+0

Предполагая, что ваши мультипликаторы будут только когда-либо однозначными, - это довольно предположение. Имейте '10' в маске, и все испортилось. –

+0

@Charles Когда я читаю вопрос OPS, SUM_MASK можно рассматривать как набор булевых. Сначала я хотел исключить умножение и просто написать '((sum + = i))' после тестирования '$ j -eq 0'. Когда я хочу поддерживать значения> 10, я должен разделить '$ Остальная_маска' на часть до и после первого', '. –

+0

Если допустимы только однозначные числа, то почему у них есть запятые, а также выражение маски как '1100000'? Наличие запятых в данных означает, что они делают что-то полезное; лучше иметь формат, который отражает фактические возможности, чем тот, который выглядит так, будто он поддерживает многозначные значения маски, но ломается, если они когда-либо используются. –

Смежные вопросы