Кратчайшая команда для расчета суммы столбца вывода на Unix? - PullRequest
47 голосов
/ 17 ноября 2008

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

Например, какой самый простой способ изменить приведенную ниже команду для вычисления и отображения итогового значения для столбца 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

Ответы [ 10 ]

82 голосов
/ 17 ноября 2008
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
13 голосов
/ 02 июня 2010

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

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

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 ' ' +) ))

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

4 голосов
/ 03 февраля 2009

У меня есть служебный скрипт, который просто складывает все столбцы. Обычно достаточно просто получить тот, который вы хотите, из вывода в одну строку. В качестве бонуса распознаются некоторые суффиксы 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
2 голосов
/ 17 февраля 2015

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

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

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 голосов
/ 17 ноября 2008

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

One-вкладыш:

import sys; print sum( [int(line.split()[6]) for line in sys.stdin.splitlines()[3:]] )
1 голос
/ 17 ноября 2008

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

ipcs | awk '
BEGIN { sum = 0 }
/0x000000/ { sum = sum + $2 }
END {print sum}'
1 голос
/ 17 ноября 2008

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

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

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

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

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

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

У меня могут быть немного неправильные флаги cut, но man ваш друг:)

0 голосов
/ 21 декабря 2018

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

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

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

0 голосов
/ 26 октября 2018

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

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

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

0 голосов
/ 13 марта 2009

Спасибо за однострочник Python выше! Это помогло мне легко проверить используемое место на моем диске. Вот смешанная оболочка / 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 / менее оболочки:

 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])"

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

...