在Unix系统中,计算输出列的总和的最短命令是什么?

51
我相信在Unix系统上有一种快速简便的方法来计算一列值的总和(可能使用类似于awkxargs的东西),但是目前我所能想到的只有编写一个逐行解析行的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
11个回答

89
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

谢谢,非常有帮助!运行该命令后,我得到了这个结果:6.59246e+08。有没有办法强制awk显示精确值(而不是科学计数法)? - An̲̳̳drew
Andrew,awk有一个printf函数:http://www.gnu.org/software/gawk/manual/gawk.html#Printf - Johannes Schaub - litb
2
printf "%d\n", sum 应该可以了。 (我猜不是%f。不知道为什么我认为它是浮点数:p) - Johannes Schaub - litb
如果您知道它始终是最后一个字段,但不想计算字段(或者字段数量不同),则可以使用print $NF。 - Jouni K. Seppänen
那真是令人不安!但非常酷......我[几乎]想要撤回我的提案:D - warren
你的第三个解决方案中 paste 部分有误。正确的命令应该是 paste -sd+ -(你忘记了末尾的 -),完整的命令为 ipcs -mb | awk 'NR > 3 { print $7 }' | paste -sd+ - | bc - s g

13

我会尝试构造一个计算字符串并将其作为参数提供给 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 ' ' +) ))

在双括号之前和之后的空格是可选的。


4

我有一个实用脚本,它可以简单地将所有列加起来。通常很容易从一行输出中获取所需的列。作为额外的奖励,还能识别一些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

使用方法:$0 [文件...] <- 没有 "-F" ... 你能澄清一下使用方法吗?支持哪些标志?

- An̲̳̳drew

3

我知道这个问题有些过时了,但是我没有看到“我的”答案,所以我还是决定发表一下。我会结合使用以下几种方法:

  • tail(获取所需的行)
  • tr(将多个连续空格缩小为一个)
  • cut(仅获取所需的列)
  • 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

Python解决方案

#!/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。

如果你想将stdin作为管道的一部分进行处理,请使用以下命令:

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

1
你可以先通过使用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
你可以在任何在线的 awk 参考资料中查找它:
ipcs | awk '
BEGIN { sum = 0 }
/0x000000/ { sum = sum + $2 }
END {print sum}'

0

要对一列中的值求和,您可以使用GNU datamash。由于前四行不包含您想要求和的值,因此我们使用tail +4将它们删除。

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

-W选项将字段分隔符设置为(可能是多个)空格。


0

感谢上面的 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

如果您想要对特定的多列进行求和,可以使用以下方法:

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

如果您想要对第1至5列进行求和,这将起作用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接