【问题标题】:BASH: Percentage change - how to calculate it? How to get absolute value without bc?BASH:百分比变化 - 如何计算?如何在没有 bc 的情况下获得绝对值?
【发布时间】:2015-01-31 18:52:21
【问题描述】:

我需要计算两个值之间的百分比变化。

我在这里的代码:

echo $time1
echo $time2
pc=$(( (($time2 - $time1)/$time1 * 100) ))
echo $pc

在控制台中带来这样的输出(使用set -xe 选项)

+ echo 1800
1800
+ echo 1000
1000
+ pc=0
+ echo 0

数学表达式中的代码似乎写得很好,但我得到了 -80 左右。为什么会发生在我身上?

问题的第二部分。我无权访问,也无法访问 bc 命令。据我所知,它可以给我我拥有的数字的绝对值。

所以.. 如果没有 bc 命令 - 这对于 IF 条件会是一个好主意吗?

if (( (( "$pc" > 20 || (( "$pc" < -20 )); then...

【问题讨论】:

  • Bash 只做整数运算,所以如果$time2 - $time1 小于$time1 那么除法总是为零。但是,如果稍微重新排序表达式,例如先乘再除,结果应该会好一些。
  • 这是否需要在纯 bash 中完成?
  • @TomFenech,不,它可以在任何适合我的工具中完成。例如可以在 perl 中完成。
  • 你看过答案了吗?您需要任何其他说明吗?

标签: bash math arithmetic-expressions


【解决方案1】:

正如您所提到的,在 bash 中没有必要这样做,我建议使用 awk:

awk -v t1="$time1" -v t2="$time2" 'BEGIN{print (t2-t1)/t1 * 100}'

通常,awk 旨在处理文件,但您可以使用BEGIN 块来执行计算,而无需将任何文件传递给它。可以使用-v 开关将Shell 变量传递给它。

如果您希望对结果进行四舍五入,您可以随时使用printf

awk -v t1="$time1" -v t2="$time2" 'BEGIN{printf "%.0f", (t2-t1)/t1 * 100}'

%.0f 格式说明符使结果四舍五入为整数(小数点后 0 位的浮点数)。

【讨论】:

  • 取决于首先如何获取值以及条件代码应该做什么,您可能能够在 awk 中完成整个事情。
  • 这是一个不错的解决方案,我赞成它,不,我将其标记为解决方案,但我需要保护自己免受某些情况的影响,所以我目前正在研究 perl 脚本。不过还是谢谢你的回答!
  • 没问题。对于涉及 shell 变量的快速浮点计算,awk 可能很有用,但它有点滥用BEGIN 块,因此对于更复杂的事情,Perl 可能是更好的选择。另一方面,如果这两个变量实际上是从一个文件中读取的,那么使用 awk 可能是最好的解决方案。
  • @TomFenech 我正在寻找一种计算百分比变化的方法。这并不重要 - 一个四舍五入的值正在完成它的工作。好的,使用bc 或其他没有bc 的方法可能更精确。我的脚本 99% 是用 bash 编写的,所以我需要一个与 bash 兼容的输出。在这种情况下 - 一个整数 :)
【解决方案2】:

使用正确的舍入、nicer 空格,并且没有多余的$ 符号:

pc=$(( ((((time2 - time1) * 1000) / time1) + (time2 > time1 ? 5 : -5)) / 10 ))

【讨论】:

    【解决方案3】:

    也许你更喜欢 bc+awk 混合线,像这样:

    total=70
    item=30
    percent=$(echo %$(echo "scale = 2; ($item / $total)" | bc -l | awk -F '.' '{print $2}'))
    echo $percent
    

    【讨论】:

    • 有用,如果 100*(time1-time2) 可能超过最大 int。
    【解决方案4】:

    一些注意事项:

    • 数学是基于整数的,所以你会提前得到零,因为你除法
    • $(( )) 语法将扩展变量,因此请使用 time1 而不是 $time1
    • 百分比答案是 -44 而不是 -80

    这里有一些例子:

    echo $(( time2-time1 )) # Output: -800
    echo $(( (time2-time1)/time1 )) # Output: 0. see it's already zero!
    echo $(( (time2-time1)/time1*100 )) # Output: 0. it's still zero and still 'incorrect'
    echo $(( (time2-time1)*100 )) # Output: -80000. fix by multiplying before dividing
    echo $(( (time2-time1)*100/time1 )) # Output: -44. this is the 'correct' answer
    echo $(( (time2-time1)*100/time2 )) # Output: -80. note this is also an 'incorrect' answer
    

    所以,简而言之,正确答案是:

    time1=1800
    time2=1000
    pc=$(( (time2 - time1) * 100 / time1 )) # -44
    

    请注意,(( 表示法 )) 也可用于处理 表达式赋值。即

    time1=1800
    time2=1000
    (( pc=((time2 - time1) * 100 / time1 ) )) # -44
    

    【讨论】:

      【解决方案5】:

      用 awk 解决:

      echo "$time1" "$time2" | awk '{print ($1-$2)/$1*100}'
      

      为避免结果为负数,我建议为time1 使用较大的数字,为time2 变量使用最小的数字。

      【讨论】:

        猜你喜欢
        • 2019-06-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-22
        • 2022-11-19
        相关资源
        最近更新 更多