【问题标题】:Finding averages from reading a file using Bash Scripting使用 Bash 脚本从读取文件中查找平均值
【发布时间】:2014-02-22 20:05:12
【问题描述】:

我正在尝试编写一个 bash 脚本来读取文件“names.txt”并计算人们的平均成绩。例如,names.txt 看起来像这样。

900706845 Harry Thompson 70 80 90
900897665 Roy Ludson 90 90 90 

脚本应该读取该行,打印出该人的 ID#、三个测试分数的平均值和相应的字母等级。所以输出需要看起来像这样

900706845 80 B
900897665 90 A

这就是我所拥有的

    #!/bin/bash
    cat names.txt | while read x
    do 
         $SUM=0; for i in 'names.txt'; do SUM=$(($SUM + $i));
         done;

         echo $SUM/3
    done

我知道回声此时只会打印出平均值,但我试图在尝试其他部分之前至少让它计算平均值。婴儿步!

【问题讨论】:

  • 一方面,for 循环并没有按照您的想法执行。试试:for i in 'names.txt'; do echo $i ; done 看看它实际上做了什么。

标签: linux bash file-io


【解决方案1】:

可能是这样的:

#!/bin/bash
while read a name1 name2 g1 g2 g3
do
   avg=$(echo "($g1+$g2+$g3)/3" | bc)
   echo $a $name1 $name2 $avg
done < names.txt

输出:

900706845 Harry Thompson 80
900897665 Roy Ludson 90

【讨论】:

  • @jaypal bc 不是 bash。
  • 同意,可以补充一点,如果 OP 遇到这样的数字,bash 不会进行浮点编号。
  • 这实际上真的帮助了我,不知道说read a name1 name2 g1 g2 g3,它会为文本的不同部分创建变量。但是发现平均值是行不通的。我将如何创建一个 avg 变量来将 3 个等级相加并除以 3。我不知道如何以正确的方式引用它们
  • @Tree55Topz 这行avg=$(echo "($g1+$g2+$g3)/3" | bc) 为你做这件事。如果你的成绩或成绩的平均值不是浮点数,你可以这样做avg=$((($g1 + $g2 + $g3)/3))
  • @Tree55Topz:avg: command not found 错误听起来像是您在作业周围添加了空格;你不能在 shell 中做到这一点。至于bc: command not found,听起来要么你搞砸了你的PATH,要么你的机器上没有bc,所以它实际上是有缺陷的。去寻找并安装它。
【解决方案2】:

根据自己的需要自定义gradeLetter

#!/bin/sh

awk '
  function gradeLetter(g)
  {
    if (g >= 90) return "A";
    if (g >= 80) return "B";
    if (g >= 70) return "C";
    if (g >= 60) return "D";
    return "E"
  }
  {
    avgGrade = ($(NF) + $(NF - 1) + $(NF - 2)) / 3;
    print $1, avgGrade, gradeLetter(avgGrade)
  }' names.txt

【讨论】:

    【解决方案3】:

    使用 awk 单行:

    awk '{ AVG = int( ( $(NF-2) + $(NF-1) + $(NF) ) / 3 ) ; if ( AVG >= 90 ) { GRADE = "A" } else if ( AVG >= 80 ) { GRADE = "B" } else if ( AVG >= 70 ) { GRADE = "C" } else if ( AVG >= 60 ) { GRADE = "D" } else { GRADE = "F" } ; print $1, AVG, GRADE }' file
    

    我们来看看细节:

    awk '{
    
      # Calculate average
      AVG = int( ( $(NF-2) + $(NF-1) + $(NF) ) / 3 )
    
      # Calculate grade
      if ( AVG >= 90 ) { GRADE = "A" }
      else if ( AVG >= 80 ) { GRADE = "B" }
      else if ( AVG >= 70 ) { GRADE = "C" }
      else if ( AVG >= 60 ) { GRADE = "D" }
      else { GRADE = "F" }
    
      print $1, AVG, GRADE
    
    }' file
    

    【讨论】:

      【解决方案4】:

      ID#s 和平均值可以通过以下方式获得:

      $ awk '{sum=0; for(i=3;i<=NF;i++) sum+=$i ; print $1, sum/3}' names.txt 
      900706845 80
      900897665 90
      

      猜测如何计算成绩,可以这样做:

      $ awk '{sum=0; for(i=3;i<=NF;i++) sum+=$i ; ave=sum/3; print $1, ave, substr("FFFFFDCBA", ave/10, 1) }' names.txt 
      900706845 80 B
      900897665 90 A
      

      上述解决方案适用于任意数量的测试,但名称仅限于 2 个单词。如果总是有三个测试,但名称可以是任意数量的单词,那么使用:

      $ awk '{ave=($(NF-2)+$(NF-1)+$NF)/3; print $1, ave, substr("FFFFFDCBA", ave/10, 1) }' names.txt
      900706845 80 B
      900897665 90 A
      

      【讨论】:

      • 你可以做awk '{print $1, ($NF+$(NF-1)+$(NF-2))/3}' name.txt,但这不是重点。 OP 正在尝试bash。并不是说我不会使用awk。 :)
      • 我的目标是即使教师给学生再次测试也能继续工作的代码。
      • 您是否也可以针对具有三个单词名称的人? ;)
      • 啊啊!!好点子! ;)。他不招收名字超过 3 个单词的学生。
      • 我只需要那一行代码吗?我试过了,它似乎没有用。单独使用和使用do while。我没有这样的文件或目录错误。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-06-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多