关于math:用于计算Unix上输出列总和的最短命令?

Shortest command to calculate the sum of a column of output on Unix?

我相信有一种快速而简单的方法可以计算Unix系统上一列值的总和(可能使用awkxargs之类的东西),但是编写一个shell脚本来逐行分析是目前唯一想到的事情。

例如,修改下面的命令以计算和显示SEGSZ列(70300)的总数的最简单方法是什么?

1
2
3
4
5
6
7
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

1
ipcs -mb | tail +4 | awk '{ sum += $7 } END { print sum }'

或无尾:

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

使用带BC的awk获得任意长结果(归功于Jouni K.):

1
ipcs -mb | awk 'NR > 3 { print $7 }' | paste -sd+ | bc


我将尝试构造一个计算字符串,并将其馈送给BC,如下所示:

  • 将包含数字的行变灰
  • 去掉每行数字前后的所有字符
  • xargs结果(得到由空格分隔的数字字符串)
  • tr将空格转换为"+"字符
  • 好胃口!
  • 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 ' ' +) ))

    双括号前后的间距是可选的。


    我有一个实用程序脚本,它简单地将所有列相加。它通常很容易从单行输出中获取所需的数据。作为奖励,一些SI后缀被识别。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #!/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"";
    }

    自定义字段分隔符示例:

    1
    2
    $ head /etc/passwd | addcol -F:
    0 0 45 39 0 0 0


    我知道这个问题有点过时,但我在这里看不到"我的"答案,所以我还是决定发布。我想结合

    • 尾巴(为了得到你需要的线条)
    • tr(将多个互易空间缩小为一个)
    • 剪切(仅获取所需列)
    • 粘贴(用+符号连接每一行)
    • BC(做实际计算)

    ipcs没有在我的系统上提供输出,所以我将用df演示它:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 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

    我知道在我的系统上做这个特别的计算是没有意义的,但是它显示了这个概念。

    此解决方案的所有部分都已在其他答案中显示,但从未在该组合中显示。


    Python溶液

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #!/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作为pipline的一部分进行处理,请使用

    1
    2
    3
    4
    import sys
    total = 0
    for line in sys.stdin:
       ...etc...

    如果您想假设始终有3个标题行:

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

    一班轮:

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

    您可以在任何在线awk参考中查找它:

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


    您可以首先通过cut运行数据,这至少会减少列。

    然后您应该能够将其导入grep,去掉非数字。

    然后…好吧,那我不确定。可能可以通过管道将其输送到bc。如果没有,则可以将它交给shell脚本来添加每个项。

    如果使用tr将换行符(
    )更改为空格(),并通过xargs将换行符导入脚本中,该脚本将循环直到没有更多的输入,然后添加每个输入,您可能会得到一个答案。

    因此,类似于以下内容:

    1
    2
    cat <whatever> | cut -d'\t` -f7 | grep -v  | tr '
    ' ' ' | xargs script-that-adds-arguments

    我可能把cut旗稍微弄错了-但man是你的朋友:)


    如果要求和的列是特定的多列,则可以使用:

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

    如果您想对第1-5列求和,这将起作用。


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

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

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


    感谢上面的python一行程序!它帮助我轻松检查我的驱动器上的已用空间。这里有一个混合的shell/python一行程序,它可以计算设备/dev/sda上的已用空间(兆字节)。我花了一段时间才发现,所以,也许有人也觉得这很有用。

    1
    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/less shell:

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

    再次感谢!