使用shell总结一个带有总计的对象列表

时间:2014-01-22 03:41:26

标签: bash shell unix

我是sh shell脚本的新手。 如何根据sortgrep编写此脚本?

有一个脚本可以按组对所有列表值进行求和。

awk '{ arr[$1]+=$2 }
     END {
       for (key in arr) printf("%s\t%s\n", key, arr[key])
     }' "$@" |
sort +0n -1

假设文件:

A 8
B 3
A 2
B 4

输出是:

A 10
B 7

我现在拥有的只是cat "$1" | sort,但如何在不使用awk的情况下单独保留左右列?

我坚持如何将第一列分别放入一个数组,其值分别在右边。

3 个答案:

答案 0 :(得分:3)

假设您有bash 4.x或最近ksh,您可以尝试:

declare -A sum   # Use typeset -A sum in ksh, which also works in bash
cat <<'EOF' |
A 8
B 3
A 2
B 4
EOF
{
while read key value
do
    ((sum[$key]+=$value))
done
for key in "${!sum[@]}"
do echo "$key ${sum[$key]}"
done
} | sort

旧式表示法+0n -1现在表示为-k 1n,1,表示按数字排序第一列(键值)。由于键值为AB,因此这不是很有帮助,因此我将关闭排序条件。对我来说更有意义的是-k 2n-k 2nr按总和递增或递减顺序排序。

bash手册建议declare -A超过typeset -A,但两者都有效; ksh需要typeset -A

答案 1 :(得分:0)

FILE="input"
eval $(sed '/^$/d;s/\([^ ]*\) \([^ ]*\)/\1=\$((\$\1 + \2))/' $FILE)
eval $(sed '/^$/d;s/ .*$//' $FILE | sort -u | sed 's/.*/echo "& = \$&";/')

第一个sed表达式以A=$(($A + 8))的形式创建行。第二个生成echo "A = $A";个表达式。

给出:

A = 10
B = 7

答案 2 :(得分:0)

使用 pure

没有外部工具且没有分支:非常快(在处理小文件时)。

declare -A values
while read name cval;do
    ((values[$name]+=cval))
  done

这将存储包含:

关联数组
set | grep ^values=
values=([A]="10" [B]="7" )

您可以使用以下方式转储:

for name in ${!values[@]};do
    printf "%s: %s\n" $name ${values[$name]}
  done
A: 10
B: 7

使用 POSIX + +

Nota:使用此问题的最后部分建议的for var in $(...)可能会更好吗使用sed的另一个shell答案

我使用sed构建两行以提交bc

sort <file |
sed '
  :a;
    $!N;
    s/^\(.*\) \(.*\)\n\1 \(.*\)/\1 \2+\3/;
   ta;
    s/^\([^ ]*\) \([^\n]*\)\($\|\n\)/"\1 ";\2;\3/;
    P;
    D;
  '|
    bc
A 10
B 7

保留最后两行,让您看到bc stream:

"A ";2+8;
"B ";3+4;

兼容回答(使用

如果目标是在 shell脚本级别处理结果:

(这是在下测试的。

for name in $(cut -d\  -f1 <file | sort -u) ;do
    value=0
    while read cnam cval ;do
        [ "$cnam" = "$name" ] && value=$((cval+value))
      done <file
    printf "%s: %d\n" $name $value
  done
A: 10
B: 7

如您所见,该文件似乎被多次访问,但是当Linux使用缓存时,只有一次读取确实发生(在处理小文件时)!

另一个答案(仅使用!)

这是一个完全不同的aproach使用强有力地解析和合并字段没有之前的sort步骤...(在很多下进行了测试):

for var in $(
    sed -ne < file 'x;G;/\(.\)=.*\n\1/!s/^\(.*\)\n\(.\) \(.*\)$/\1 \2=\3 /;
        s/\(.\)=\(.*\)\n\1 \(.*\)/\1=\3+\2/;x;${x;p}')
  do
    printf "%s: %3d\n" ${var%=*} $(( ${var#*=} ))
  done
A:  10
B:   7

其中 varname ${var%=*},由符号+合并在${var#*=}中:

sed -ne < file '
  x;      # swap crt line and hold space
  G;      # append hold space to crt line
  /\(.\)=.*\n\1/ ! s/^\(.*\)\n\(.\) \(.*\)$/\1 \2=\3 /; # if not find, append
  s/\(.\)=\(.*\)\n\1 \(.*\)/\1=\3+\2/; # add `+` + crt value at right place
  x;      # swap crt line and hold space
  ${   # on last line
    x;    # swap crt line and hold space
    p     # print
  }'   # that's all folks!
 A=2+8  B=4+3