我正试图循环浏览目录中的文件,以查找动物及其价值。该命令应该只显示动物和总值。例如:
File1具有:
Monkey 11
Bear 4
File2具有:
Monkey 12
如果我想要猴子的总价值,那么我会这样做:
for f in *; do
total=$(grep $animal $f | cut -d " " -f 2- | paste -sd+ | bc)
done
echo $animal $total
这将返回正确的值:
Monkey 23
然而,如果只有一个动物的例子,比如Bear,变量total不会返回任何值,我只会得到回应:
Bear
为什么会出现这种情况,我该如何解决?
注意:我不允许使用find
命令。
您可以使用这个小awk
而不是for
grep
cut
paste
bc
:
awk -v animal="Bear" '
$1 == animal { count += $2 }
END { print count + 0 }
' *
关于OP关于代码为何如此行为的问题的评论:
total
在每次通过循环时重置,因此- 离开循环后,
total
将处理"最后"文件中的计数 - 在
Bear
的情况下,处理的"最后一个"文件是File2
,由于File2
不包含Bear
的任何条目,我们得到total=''
,它是由echo
打印的 - 如果
Bear
条目从File1
移动到File2
,则OP的代码应打印Bear 4
- OP的当前代码有效地忽略了所有输入文件,并打印"最后"文件中的任何内容(在本例中为
File2
(
OP的当前代码生成以下内容:
# Monkey
Monkey 12 # from File2
# Bear
Bear # no match in File2
我可能会选择用一个awk
(1x子流程(调用来替换整个grep/cut/paste/bc
(4x个子流程((假设没有匹配,我们报告0
(:
for animal in Monkey Bear Hippo
do
total=$(awk -v a="${animal}" '$1==a {sum+=$2} END {print sum+0}' *)
echo "${animal} ${total}"
done
这将生成:
Monkey 23
Bear 4
Hippo 0
注意:
- 我假设OP的实际代码比
echo
对stdout的计数做得更多,因此需要total
变量,否则我们可以消除total
变量,并让awk
直接将动物/总和对打印到stdout - 如果OP的真实代码有一个处理动物列表的父循环,那么单个
awk
调用可能同时处理所有动物;目的是使CCD_ 29生成完整的动物/总和对集合,然后可以将其馈送到循环构建体;如果是这种情况,并且OP在实现单个awk
解决方案时存在一些问题,则应该提出一个新问题
为什么是
grep
不输出任何内容,因此不会通过管道传播任何内容,并且会将空字符串分配给total
。
因为total
在每个循环中都被重置(total=anything
没有引用以前的值(,所以它只有最后一个文件的值。
如何修复它?
不要试图一次做所有的事情,一次做更少的事情。
total=0
for f in *; do
count=$(grep "$animal" "$f" | cut -d " " -f 2-)
total=$((total + count)) # reuse total, reference previous value
done
echo "$animal" "$total"
一个精通shell的程序员很可能会因为这样的问题而求助于AWK。记得用shellcheck检查你的脚本。
有了你想要做的,你可以一次完成所有文件:
total=$(
{
echo 0 # to have at least nice 0 if animal is not found
grep "$animal" * |
cut -d " " -f 2-
} |
paste -sd+ |
bc
)
只需bash:
declare -A animals=()
for f in *; do
while read -r animal value; do
(( animals[$animal] = ${animals[$animal]:-0} + value ))
done < "$f"
done
declare -p animals
输出
declare -A animals=([Monkey]="23" [Bear]="4" )
使用这种方法,您可以通过处理每个文件一次来获得所有动物的所有总数
$ head File*
==> File1 <==
Monkey 11
Bear 4
==> File2 <==
Monkey 12
==> File3 <==
Bear
Monkey
使用awk和bash阵列
#!/bin/bash
sumAnimals(){
awk '
{ NF == 1 ? a[$1]++ : a[$1]=a[$1]+$2 }
END{
for (i in a ) printf "[%s]=%dn",i, a[i]
}
' File*
}
# storing all animals in bash array
declare -A animalsArr="( $(sumAnimals) )"
# show array content
declare -p animalsArr
# getting total from array
echo "Monkey: ${animalsArr[Monkey]}"
echo "Bear: ${animalsArr[Monkey]}"
输出
declare -A animalsArr=([Bear]="5" [Monkey]="24" )
Monkey: 24
Bear: 5