it-swarm.com.ru

Самая короткая команда для вычисления суммы столбца вывода на Unix?

Я уверен, что есть быстрый и простой способ вычислить сумму столбца значений в системах Unix (возможно, используя что-то вроде awk или xargs), но единственное, что написание скрипта Shell для построчного анализа строк приходит на ум в данный момент.

Например, как проще всего изменить приведенную ниже команду, чтобы вычислить и отобразить сумму для столбца SEGSZ (70300)?

ipcs -mb | head -6
IPC status from /dev/kmem as of Mon Nov 17 08:58:17 2008
T         ID     KEY        MODE        OWNER     GROUP      SEGSZ
Shared Memory:
m          0 0x411c322e --rw-rw-rw-      root      root        348
m          1 0x4e0c0002 --rw-rw-rw-      root      root      61760
m          2 0x412013f5 --rw-rw-rw-      root      root       8192
47
An̲̳̳drew
ipcs -mb | tail +4 | awk '{ sum += $7 } END { print sum }'

Или без хвоста

ipcs -mb | awk 'NR > 3 { sum += $7 } END { print sum }'

Использование awk с bc для получения произвольных длинных результатов (кредитов Jouni K.):

ipcs -mb | awk 'NR > 3 { print $7 }' | paste -sd+ | bc
82
Johannes Schaub - litb

Я бы попытался создать строку вычисления и передать ее в bc следующим образом:

  1. grep строки, содержащие числа
  2. sed удаляет все символы до (и после) числа в каждой строке
  3. xargs результат (чтобы получить строку чисел, разделенных пробелами)
  4. tr переводит пробелы в символы '+'
  5. хорошего аппетита bc!

ipcs -mb | grep -w '^m ' | sed 's/^.*\s//' | xargs | tr ' ' + | bc

Похоже, это немного длиннее, чем решение awk, но для всех, кто не может прочитать (и понять) нечетный код awk, это может быть легче понять ... :-)

Если bc не установлено, вы можете использовать двойные скобки в шаге 5 выше для вычисления результата:

  • echo $(( $(ipcs -mb | grep -w '^m ' | sed 's/^.*\s//' | xargs | tr ' ' +) )) или
  • SUM=$(( $(ipcs -mb | grep -w '^m ' | sed 's/^.*\s//' | xargs | tr ' ' +) )) или
  • (( SUM=$(ipcs -mb | grep -w '^m ' | sed 's/^.*\s//' | xargs | tr ' ' +) ))

Интервал после и перед двойными скобками не является обязательным.

13
Peterino

У меня есть служебный скрипт, который просто складывает все столбцы. Обычно достаточно просто получить тот, который вы хотите, из вывода в одну строку. В качестве бонуса распознаются некоторые SI-суффиксы.

#!/usr/bin/awk -f
# Sum up numerical values by column (white-space separated)
#
# Usage:  $0 [file ...]
#
# stern, 1999-2005

{
    for(i = 1; i <= NF; ++i) {
        scale = 1
        if ($i ~ /[kK]$/) { scale = 1000 }
        if ($i ~ /[mM]$/) { scale = 1000*1000 }
        if ($i ~ /[gG]$/) { scale = 1000*1000*1000 }
        col[i] += scale * $i;
    }
    if (NF > maxnf) maxnf = NF;
}

END {
    for(i = 1; i <= maxnf; ++i) { printf " %.10g", col[i] }
    print "";
}

Пример с пользовательским разделителем полей:

$ head /etc/passwd | addcol -F:
0 0 45 39 0 0 0
4
user61853

Python Solution

#!/usr/bin/env python
text= file("the_file","r")
total= 0
for line in text:
    data = line.split()
    if data[0] in ('T', 'Shared', 'IPC'): continue
    print line
    segsize= int(data[6])
    total += segsize
print total

Большинство дистрибутивов Linux имеют Python.

Если вы хотите обработать стандартный ввод как часть трубопровода, используйте

import sys
total = 0
for line in sys.stdin:
   ...etc...

Если вы хотите предположить, что всегда есть 3 строки заголовка:

import sys
total = 0
for line in sys.stdin.readlines()[3:]:
    total += int(line.split()[6])
print total

Один лайнер:

import sys; print sum( [int(line.split()[6]) for line in sys.stdin.splitlines()[3:]] )
2
S.Lott

Я знаю, что этот вопрос несколько устарел, но я не вижу здесь «моего» ответа, поэтому я решил опубликовать его. Я бы пошел с комбинацией

  • хвост (чтобы получить линии, которые вам нужны)
  • tr (чтобы уменьшить количество последовательных пробелов до одного)
  • вырезать (чтобы получить только нужный столбец)
  • вставить (чтобы объединить каждую строку со знаком +)
  • до н.э. (чтобы сделать фактический расчет)

ipcs не дает вывод в моей системе, поэтому я просто продемонстрирую его с помощью df:

# df
Filesystem     1K-blocks    Used Available Use% Mounted on
rootfs          33027952 4037420  27312812  13% /
udev               10240       0     10240   0% /dev
tmpfs             102108     108    102000   1% /run
/dev/xvda1      33027952 4037420  27312812  13% /
tmpfs               5120       0      5120   0% /run/lock
tmpfs             204200       0    204200   0% /run/shm
/dev/xvda1      33027952 4037420  27312812  13% /var/www/clients/client1/web1/log
/dev/xvda1      33027952 4037420  27312812  13% /var/www/clients/client1/web2/log
/dev/xvda1      33027952 4037420  27312812  13% /var/www/clients/client1/web3/log
/dev/xvda1      33027952 4037420  27312812  13% /var/www/clients/client1/web4/log
/dev/xvda1      33027952 4037420  27312812  13% /var/www/clients/client2/web5/log
/dev/xvda1      33027952 4037420  27312812  13% /var/www/clients/client2/web6/log
# df | tail -n +2 | tr -s ' ' | cut -d ' ' -f 2 | paste -s -d+ | bc
264545284

Я знаю, что делать конкретные вычисления в моей системе на самом деле не имеет смысла, но это показывает концепцию.

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

2
Alexander Stumpf

Вы могли бы начать, пропустив данные через cut - который по крайней мере урезал бы столбцы. 

Затем вы сможете передать это в grep, удаляя нечисловые числа.

Тогда ... ну, тогда я не уверен. Может быть возможно передать это bc. Если нет, то он, безусловно, может быть передан сценарию Shell для добавления каждого элемента.

Если вы использовали tr, чтобы заменить символы новой строки (\n) на пробелы (), и передавали это через xargs в свой сценарий, который циклически повторяется до тех пор, пока больше нет входных данных, добавляя каждый из них, у вас может быть ответ.

Итак, что-то похожее на следующее:

cat <whatever> | cut -d'\t` -f7 | grep -v <appropriate-character-class> | tr '\n' ' ' | xargs script-that-adds-arguments

Возможно, у меня немного неправильные флаги cut, но man ваш друг :)

1
warren

Вы можете посмотреть это в любой онлайн-ссылке на awk:

ipcs | awk '
BEGIN { sum = 0 }
/0x000000/ { sum = sum + $2 }
END {print sum}'
1
florin

Спасибо за однострочник Python выше! Это помогло мне легко проверить используемое пространство на моем диске .... Вот смешанная однострочная оболочка Shell/Python, которая делает это - подсчитывает используемое пространство на устройстве/dev/sda в мегабайтах. Мне потребовалось некоторое время, прежде чем я узнал об этом, так что, может быть, кто-то тоже найдет это полезным.

df -h -B 1M | grep dev/sda | tr -s ' '| cut -d' ' -f3 |python -c "import sys; print sum([int(num) for num in sys.stdin.readlines()])"

или больше Python/меньше Shell:

 df -h -B 1M | python -c "import sys; print sum([int(l.split()[2]) for l in sys.stdin.readlines() if '/dev/sda' in l])"

Еще раз спасибо!

0
Andrzej

Для суммирования значений в столбце вы можете использовать GNU datamash. Поскольку первые четыре строки не содержат значений, которые вы хотите суммировать, мы удаляем их с помощью tail +4.

ipcs -mb  | tail +4 | datamash -W sum 7

Параметр -W устанавливает разделитель поля в (возможно, несколько) пробелов.

0
Socowi

Если у вас есть несколько столбцов, которые вы хотите суммировать, вы можете использовать:

input_command | awk '{s1+=$1;s2+=$2;s3+=$3;s4+=$4;s5+=$5}END{print s1,s2,s3,s4,s5}'

который будет работать, если вы хотите сложить столбцы 1–5.

0
gerrit