【问题标题】:Averaging lines without awk using bash使用 bash 对没有 awk 的行进行平均
【发布时间】:2016-02-02 23:33:08
【问题描述】:

我刚开始使用 bash,但在 bash 算术方面遇到了一些困难。假设我有一些城市有三个温度,我想在不使用 awk 命令的情况下平均温度。我怎样才能做到这一点?我已经用 awk 完成了,但我正在练习不同的技术。

# cityTemp.txt with four cities, three day's temperatures
Toronto 20 25 30
Miami 80 80 110
London 40 20 60
New York 5 10 15 

假设我正在尝试编写一个脚本来输出:

25 Toronto
90 Miami
40 London
10 New York

我已经用一些管道和 awk 命令完成了这项工作,但我在不使用 awk 的情况下执行此操作时遇到了问题。

再说一次,我是新手。我尝试了一个 for 循环,但我真的不知道自己在做什么。

---编辑1:

Benjamin W. 我正在再次编辑,但现在我正在循环播放:

#!/bin/bash

for i in $(cat $1)
do
    echo "i is: ${i}"
done < $1

很明显,这只是一次打印 cityTemp.txt 中的每个字段。

---编辑2:

这是我的结束尝试

while read -a rows
do
    total=0
    sum=0
    for i in "${rows[@]}"
    do
        sum=`expr $sum + $i`
        total=`expr $total + 1`
    done
    average=`expr $sum / $total`
    Output = ${average}
done < $1
echo ${averages}

【问题讨论】:

  • 你的 for 循环是什么样的?怎么没用?您可以edit您的问题添加一些信息。
  • 感谢您的回复。我有一些事情要做,但没有什么是很有效的。我又来了。我之前尝试使用 expr(),但我很难只查看第 2、3、4 列
  • 温度是否总是 3 个值,或者你需要一些东西来处理任意数量的温度
  • 我只使用了 3 个值,但城市数量不同(始终格式化为:city temp temp temp),但如果您可以应用范围,我当然想学习。 --edit-- 我确实在那里放了一个邮政编码,我把它放在括号里,但我只得到了所有这些都可以使用 awk
  • 您的最终版本依赖于这样一个事实,即字符串在算术上下文中被评估为零,因为您将所有城市名称视为数字;参看。 i=York; echo $(( i + 1 ))。它有效,但我不确定您是否知道发生这种情况。

标签: linux bash shell


【解决方案1】:

这应该可以在任何 Posix shell 中工作,所以我们也可以将shebang#!/bin/sh. 一起使用

#!/bin/sh

avg () {
  city=
  while [ $# -gt 3 ]; do
    city="$city $1"
    shift
  done
  echo $((($1 + $2 + $3) / 3)) $city
}

while read line; do
  avg $line
done < cityTemp.txt

如果城市在每条线上都是最后一个,这会容易得多,哈哈。

【讨论】:

  • 是的,请参阅 test(1) 别名为 [ 。 . . 我正在积累单词,以便名称中带有空格的城市工作。
  • 天哪,谢谢。就这样我知道发生了什么, avg() 你是在这个范围内创建一个函数吗?然后你调用它并传递一行
  • 是的。通过将大部分代码放在 shell 函数中,我可以使用 shift 运算符。
  • 啊,我明白了!整洁。
  • 我假设轮班过程是in this forum
【解决方案2】:

这应该适用于具有任意行数和 3 个团队预置输入的文件:

#!/bin/bash
i=1
file=file
nolines=`wc -l $file | cut -d' ' -f1`
while [ $i -lt $nolines ]
do
line=$(sed -n "$i"p $file)
city=$(echo $line | cut -d' ' -f1)
val1=$(echo $line | cut -d' ' -f2)
val2=$(echo $line | cut -d' ' -f3)
val3=$(echo $line | cut -d' ' -f4)
sum=$(($val1 + $val2 + $val3))
avg=$(($sum / 3))
echo $avg $city
i=$[i+1]
done

【讨论】:

    【解决方案3】:

    这利用了一些 Bash 功能,但最终变得相当庞大:

    #!/bin/bash                                                                                                                         
    
    # Regex to capture city name and temperatures                                                                                       
    re='([[:alpha:] ]*) ([[:digit:] ]*)'                                                                                                
    
    while IFS= read -r line; do                                                                                                         
        unset temps                             # Delete array                                                                                                  
        [[ $line =~ $re ]]                      # Capture city name and temperatures
        city="${BASH_REMATCH[1]}"               # Assign city name                                                                              
        read -a temps <<< "${BASH_REMATCH[2]}"  # Assign temperatures to array                                                          
    
        sum=0                                                                                                                           
    
        # Loop over temperatures                                                                                                        
        for temp in "${temps[@]}"; do                                                                                                   
            (( sum += temp ))                                                                                                           
        done                                                                                                                            
    
        # Print average                                                                                                                 
        printf "%d %s\n" $(( sum / ${#temps[@]} )) "$city"                                                                              
    done < "$1"
    

    如果城市名称总是由相同数量的单词组成,但有“纽约”,那会简单得多,所以我使用正则表达式来分隔名称和温度。

    然后将温度分配给带有read -a 的数组,我在该数组上循环以获取总数。

    分配给要循环的数组可以通过直接循环字符串并依赖分词来代替:

    for temp in ${BASH_REMATCH[2]}; do
    

    但是我们必须通过更新计数器或类似的东西来跟踪温度值的数量。

    【讨论】:

      【解决方案4】:

      这是一个使用 Bash 的内置正则表达式在城市名称后添加任意元素的版本。

      给定:

      $ echo "$nums"
      Toronto 20 25 30
      Miami 80 80 110
      London 40 20 60
      New York 5 10 15
      

      你可以这样做:

      regex="^([a-zA-Z ]+)(.+)$"
      echo "$nums" | while read line
      do
          [[ $line =~ $regex ]]
          name="${BASH_REMATCH[1]}"
          arr=(${BASH_REMATCH[2]})
          sum=0
          for i in ${arr[@]}; do
              let sum+=$i
          done
          printf "%s %s\n" $(( $sum / ${#arr[@]} )) "$name"
      done
      

      打印:

      25 Toronto 
      90 Miami 
      40 London 
      10 New York 
      

      如果你想要浮点数,你可以像这样使用bc

      给定:

      $ echo "$nums"
      Toronto 22 25 30
      Miami 80 80 110
      London 40 20 60
      New York 5 10 15
      

      你可以这样做:

      echo "$nums" | while read line
      do
          [[ $line =~ $regex ]]
          name="${BASH_REMATCH[1]}"
          arr=(${BASH_REMATCH[2]})
          avg=$(IFS='+'; echo "scale=2;(${arr[*]})/${#arr[@]}" | bc -l)
          printf "%s %s\n" $avg "$name"   
      done
      

      打印:

      25.66 Toronto 
      90.00 Miami 
      40.00 London 
      10.00 New York 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多