我是sh shell脚本的新手。
如何根据sort
和grep
编写此脚本?
有一个awk脚本可以按组对所有列表值进行求和。
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
的情况下单独保留左右列?
我坚持如何将第一列分别放入一个数组,其值分别在右边。
答案 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
,表示按数字排序第一列(键值)。由于键值为A
和B
,因此这不是很有帮助,因此我将关闭排序条件。对我来说更有意义的是-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)
没有外部工具且没有分支:非常快(在处理小文件时)。
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
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脚本级别处理结果:
(这是在bash,ksh,dash和busybox下测试的。
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使用sed强有力地解析和合并字段没有之前的sort
步骤...(在很多shell下进行了测试):
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