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

时间:2008-11-17 15:05:18

标签: unix math shell ksh

我确信有一种快速简便的方法可以计算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 个答案:

答案 0 :(得分:82)

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

答案 1 :(得分:13)

我会尝试构建一个计算字符串并将其提供给 bc ,如下所示:

  1. grep 包含数字的行
  2. sed 离开每行上的数字之前(和之后)的所有字符
  3. xargs 结果(获取由空格分隔的数字字符串)
  4. tr 将空白回复为“+”字符
  5. 胃口好 bc
  6. 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 ' ' +) ))

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

答案 2 :(得分: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

答案 3 :(得分: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作为pipline的一部分进行处理,请使用

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

答案 4 :(得分:2)

我知道这个问题有些陈旧,但我在这里看不到“我的”答案,所以我决定发帖。

组合使用
  • tail(获取你需要的行)
  • tr(将多个连续空格缩小为一个)
  • cut(仅获取所需的列)
  • 粘贴(用+符号连接每一行)
  • 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

我知道在我的系统上进行这种特殊计算并没有多大意义,但它显示了这个概念。

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

答案 5 :(得分: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是您的朋友:)

答案 6 :(得分:1)

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

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

答案 7 :(得分: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 / less 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])"

再次感谢!

答案 8 :(得分:0)

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

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

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

答案 9 :(得分:0)

如果要累加多个特定列,可以使用:

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

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

答案 10 :(得分:0)

不可思议的perl如何被证明!

请参阅perldoc perlrun中的-a(表示-n)。 还有perldoc perlvar来了解$。和朋友。

$ df |perl -aE'$.<2or$u+=$F[2]}{say"Used: $u"'
Used: 129016836

如果您真的想发疯:

$ df -h |perl -anE'$|=1;
  BEGIN{%M=(""=>1,k=>1e3,K=>2**10,M=>2**20,G=>2**30,T=>2**40);%D=reverse%M}
  print;
  if($.<2){@V=map length(),/\s*+[^a-z]\S*(?:\s+[a-z]+)*/g;next} # parse header
  ($w=($_==$#V)+length($F[$_])-$V[$_])>0 and do{$V[$_]+=$w;$_<$#V and $V[$_+1]-=$w} for 0..$#F; # optimize column widths
  $S[$_]+=($F[$_]=~/^(\d+(?:[.]\d*)?)([kKMGT])?$/aa?$1*$M{($D||=$2)&&$2}:-Inf)for 0..$#F; # scale numeric values
}{ # show results
  say join("",map+("-"x($V[$_]-1)).($S[$_]<0?"^":"+"),0..$#V);
  $V[$_]+=$V[$_-1]for 1..$#V;
  if($D){for$s(@S){@s=sort{$b<=>$a}grep{$_<$s}keys%D and$s=sprintf"%.1f%s",$s/$s[0],$D{$s[0]}}}
  say sprintf+("%s%*s"x@S),map{((!$p||($_>0 and length($S[$_])>=($w=($V[$_]-$V[$_-1])))?(($q?"\n":(($p=$q=1)&&"")),$V[$_]):("",0+$w)),$S[$_])}grep{$S[$_]!=-Inf}0..$#S;
'