将命令值存储到变量Bash Script中



我正试图循环浏览目录中的文件,以查找动物及其价值。该命令应该只显示动物和总值。例如:

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而不是forgrepcutpastebc:

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

最新更新