在 bash 中,我可以从日志文件中 grep 一些时间测量值,如下所示
grep "time:" myLogfile.txt | cut -d' ' -f 3 >> timeMeasurements.txt
#timeMeasurements.txt
2.5
3.5
2.0
...
现在我想根据中的值计算平均值timeMeasurements.txt
。在 bash 中执行此操作的最快方法是什么?
我知道有 gnuplot 和 R,但似乎必须为它们中的任何一个编写一些冗长的脚本。
答案1
必修GNU 数据聚合版本
$ datamash mean 1 < file
2.6666666666667
在旁边:感觉确实如此应该有可能天生地bc
(即不使用 shell 或外部程序来循环输入值)。GNU实现bc
包含一个read()
函数 - 但是让它检测输入结束似乎非常困难。我能想到的最好的办法是:
#!/usr/bin/bc
scale = 6
while( (x = read()) ) {
s += x
c += 1
}
s/c
quit
然后你可以通过管道将文件输入到只要你用任何非数字字符终止输入例如
$ { cat file; echo '@'; } | ./mean.bc
2.666666
答案2
您可以使用awk
。Bash 本身不太擅长数学......
awk 'BEGIN { lines=0; total=0 } { lines++; total+=$1 } END { print total/lines }' timeMeasurements.txt
笔记
lines=0; total=0
将变量设置为 0lines++
每行增加lines
一total+=$1
将每行的值添加到累计总数中print total/lines
完成后,将总数除以值的数量
答案3
另一种方法是使用sed
和bc
:
sed 's/^/n+=1;x+=/;$ascale=1;x/n' timemeasurements.txt | bc
sed 表达式将输入转换为如下内容:
n+=1;x+=2.5
n+=1;x+=3.5
n+=1;x+=2.0
scale=1;x/n
它通过bc
管道逐行进行评估。
答案4
您可以使用bc
基本计算器,循环while
如下read
:
count=0; sum=0; while read -r num; do ((count++)); sum=$(echo "$sum + $num" | bc); done < timeMeasurement.txt; echo "scale=2; $sum / $count" | bc -l
或者更易读的是:
count=0
sum=0
while read -r num
do
((count++))
sum=$(echo "$sum + $num" | bc)
done < timeMeasurement.txt
echo "scale=2; $sum / $count" | bc -l
解释:
- 首先,我们将值的数量和总和设置为变量 count 和 sum,值为 0。
- 逐行读取文件,将行中的值设置为变量 num。我们使用构造
while read -r num; do ... ; done < timeMeasurements.txt
来执行此操作。这意味着我们将对文件的每一行执行某些操作。 - 在 while 循环中,使用 bash 算术将每行计数变量增加一
((count++))
。 $(...)
使用带echo
管道的bash 命令替换将bc
文件此行的 num 变量的值添加到所有先前行的 num 变量的总和中。bc
因为 bash 不能很好地处理浮点运算。
此时循环结束,count变量包含时间测量值的数量,sum变量包含时间测量的总和。
- 与我们的变量一起使用
echo
来创建传递给的平均值计算bc
。该scale=2
部分告诉bc
要显示多少个有效数字。