在不使用“bc”的情况下计算Shell脚本中的舍入百分比

我正在尝试计算Shell脚本中某些项目的百分比。 我想把价值四舍五入,即如果结果是59.5,我应该期望60而不是59。

item=30 total=70 percent=$((100*$item/$total)) echo $percent 

这给了42。

但实际上,结果是42.8,我会把它四舍五入到43.“BC”做的伎俩,有没有办法不使用“BC”?

我没有授权安装任何新的软件包。 “dc”和“bc”在我的系统中不存在。 它应该是纯粹的Shell,不能使用Perl或Python脚本

使用AWK(不bash-isms):

 item=30 total=70 percent=$(awk "BEGIN { pc=100*${item}/${total}; i=int(pc); print (pc-i<0.5)?i:i+1 }") echo $percent 43 

以2 *原始百分比计算并得到2的模数提供四舍五入的增量。

 item=30 total=70 percent=$((200*$item/$total % 2 + 100*$item/$total)) echo $percent 43 

(用bash,ash,dash和ksh测试)

这是一个比AWK协同处理更快的实现:

 $ pa() { for i in `seq 0 1000`; do pc=$(awk "BEGIN { pc=100*${item}/${total}; i=int(pc); print (pc-i<0.5)?i:i+1 }"); done; } $ time pa real 0m24.686s user 0m0.376s sys 0m22.828s $ pb() { for i in `seq 0 1000`; do pc=$((200*$item/$total % 2 + 100*$item/$total)); done; } $ time pb real 0m0.035s user 0m0.000s sys 0m0.012s 

符合POSIX的shell脚本仅限于使用shell语言的 整数运算( “仅需要有符号长整型运算” ),因此纯shell解决方案必须模拟浮点运算:

 item=30 total=70 percent=$(( 100 * item / total + (1000 * item / total % 10 >= 5 ? 1 : 0) )) 
  • 100 * item / total以百分比的形式产生整数除法的截断结果。
  • 1000 * item / total % 10 >= 5 ? 1 : 0 1000 * item / total % 10 >= 5 ? 1 : 0计算第一位小数,如果等于或大于5,则将整数结果加1以使其四舍五入。
  • 注意如何在算术扩展$((...))内不需要在变量引用前添加$

如果 – 与问题的前提相矛盾 – 使用外部公用事业是可以接受的:


  • awk提供了一个简单的解决方案,然而,它提出了使用真正的双精度二进制浮点值的警告 ,因此可能会产生十进制表示形式的意外结果 – 例如,尝试printf '%.0f\n' 28.528而不是预期的29 ):
 awk -v item=30 -v total=70 'BEGIN { printf "%.0f\n", 100 * item / total }' 
  • 请注意-v是如何用于为awk脚本定义awk变量的,这允许在单引号的 awk脚本和因此的awk脚本之间进行干净的分离,并且从shell中传递给它的任何值。

  • 相比之下,尽管bc 一个POSIX实用程序 (因此可以期望在大多数类Unix平台上出现),并执行任意精度算术,但它总是截断结果,因此四舍五入必须由另一个实用程序执行; 然而, printf ,即使它原则上是POSIX实用程序 , 也不需要支持浮点格式说明符(例如上面的awk使用的),所以下面的代码可能工作也可能不工作 (并且不值得麻烦,更简单的awk解决方案,并且由于浮点算法导致的精度问题又回到了图中):
 # !! This MAY work on your platform, but is NOT POSIX-compliant: # `-l` tells `bc` to set the precision to 20 decimal places, `printf '%.0f\n'` # then performs the rounding to an integer. item=20 total=70 printf '%.0f\n' "$(bc -l <<EOF 100 * $item / $total EOF )" 

以下是基于我的第二个答案,但是用exprback-ticks (尽管对大多数人来说可能是可恶的)可以适应在本地最古老的炮弹中工作:

 item=30 total=70 percent=`expr 200 \* $item / $total % 2 + 100 \* $item / $total` echo $percent 43