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

use*_*302 15 linux shell percentage

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

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

echo $percent
Run Code Online (Sandbox Code Playgroud)

这给了42.

但实际上,结果是42.8,我想把它四舍五入到43."bc"可以解决问题,有没有办法不使用"bc"?

我无权安装任何新软件包.我的系统中没有"dc"和"bc".它应该是纯粹的Shell,也不能使用perl或python脚本

Mic*_*ack 25

使用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
Run Code Online (Sandbox Code Playgroud)


Mic*_*ack 8

取2*原始百分比计算并获得其中的模数2提供舍入的增量.

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

echo $percent
43
Run Code Online (Sandbox Code Playgroud)

(用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
Run Code Online (Sandbox Code Playgroud)


mkl*_*nt0 7

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

item=30
total=70

percent=$(( 100 * item / total + (1000 * item / total % 10 >= 5 ? 1 : 0) ))
Run Code Online (Sandbox Code Playgroud)
  • 100 * item / total整数除法的截断结果作为百分比.
  • 1000 * item / total % 10 >= 5 ? 1 : 0 计算第一个小数位,如果它等于或大于5,则将整数结果加1,以便将其四舍五入.
  • 注意如何有没有必要与前缀变量引用$的算术扩展中$((...)).

如果 - 与问题的前提相矛盾 - 使用外部设施是可以接受的:


  • awk提供了一个简单的解决方案,然而,它需要注意它使用真正的双精度二进制浮点值,因此可能会产生十进制表示的意外结果 - 例如,尝试printf '%.0f\n' 28.5,产生28而不是预期29):
awk -v item=30 -v total=70 'BEGIN { printf "%.0f\n", 100 * item / total }'
Run Code Online (Sandbox Code Playgroud)
  • 注意如何-v被用来定义为变量awk脚本,其允许完全分离单引号,因此文字 awk脚本和从传递给它的任何值.

  • 相比之下,即使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
)"
Run Code Online (Sandbox Code Playgroud)