我试图在 Bash 脚本中划分两个图像宽度,但 bash 给了我0
结果:
RESULT=$(($IMG_WIDTH/$IMG2_WIDTH))
我确实研究了 Bash 指南,我知道我应该bc
在他们使用的互联网上的所有示例中使用bc
。在echo
我试图把同样的东西放在我的,SCALE
但它没有用。
这是我在教程中找到的示例:
echo "scale=2; ${userinput}" | bc
我怎样才能让 Bash 给我一个浮点数0.5
?
我试图在 Bash 脚本中划分两个图像宽度,但 bash 给了我0
结果:
RESULT=$(($IMG_WIDTH/$IMG2_WIDTH))
我确实研究了 Bash 指南,我知道我应该bc
在他们使用的互联网上的所有示例中使用bc
。在echo
我试图把同样的东西放在我的,SCALE
但它没有用。
这是我在教程中找到的示例:
echo "scale=2; ${userinput}" | bc
我怎样才能让 Bash 给我一个浮点数0.5
?
你不能。bash只做整数;您必须委托给一个工具,例如bc
.
你可以这样做:
bc <<< 'scale=2; 100/3'
33.33
更新 20130926
:您可以使用:
bc -l <<< '100/3' # saves a few hits
33.33333333333333333333
正如其他人所指出的,bash
不支持浮点运算,尽管您可以使用一些固定的小数技巧来伪造它,例如使用两个小数:
echo $(( 100 * 1 / 3 )) | sed -e 's/..$/.&/;t' -e 's/.$/.0&/'
输出:
.33
有关类似但更简洁的方法,请参阅Nilfred 的答案。
除了提到的bc
和awk
替代方案之外,还有以下内容:
clisp -x '(/ 1.0 3)'
清理输出:
clisp --quiet -x '(/ 1.0 3)'
或通过stdin
:
echo '(/ 1.0 3)' | clisp --quiet | tail -n1
echo 2k 1 3 /p | dc
echo 1/3.0 | genius
echo 1 3 div = | gs -dNODISPLAY -dQUIET | sed -n '1s/.*>//p'
echo 'pr 1/3.' | gnuplot
convert xc: -format '%[fx:1/3]' info:
或通过stdin
:
echo 1/3 | { convert xc: -format "%[fx:$(cat)]" info:; }
jq -n 1/3
或通过stdin
:
echo 1/3 | jq -nf /dev/stdin
echo 'print $(( 1/3. ))' | ksh
lua -e 'print(1/3)'
或通过标准输入:
echo 'print(1/3)' | lua
echo '1/3,numer;' | maxima
清理输出:
echo '1/3,numer;' | maxima --quiet | sed -En '2s/[^ ]+ [^ ]+ +//p'
echo 1/3 | node -p
echo 1/3 | octave
echo print 1/3 | perl
echo print 1/3. | python2
echo 'print(1/3)' | python3
echo 1/3 | R --no-save
清理输出:
echo 1/3 | R --vanilla --quiet | sed -n '2s/.* //p'
echo puts 1/3.0 | ruby
units 1/3
紧凑的输出:
units --com 1/3
echo 1/3 | wcalc
清理输出:
echo 1/3 | wcalc | tr -d ' ' | cut -d= -f2
print $(( 1/3. ))
或通过stdin
:
echo 'print $(( 1/3. ))' | zsh
Stéphane Chazelas 在 UL 上回答了类似的问题。
稍微改进一下马文的答案:
RESULT=$(awk "BEGIN {printf \"%.2f\",${IMG_WIDTH}/${IMG2_WIDTH}}")
bc 并不总是以安装包的形式出现。
-l
您可以通过选项(L 字母)使用 bc
RESULT=$(echo "$IMG_WIDTH/$IMG2_WIDTH" | bc -l)
作为 bc 的替代方法,您可以在脚本中使用 awk。
例如:
echo "$IMG_WIDTH $IMG2_WIDTH" | awk '{printf "%.2f \n", $1/$2}'
在上面,“%.2f”告诉 printf 函数返回一个小数点后两位的浮点数。我使用 echo 将变量作为字段通过管道传递,因为 awk 在它们上正常运行。“ $1 ”和“ $2 ”指的是输入到 awk 的第一个和第二个字段。
您可以使用以下方法将结果存储为其他变量:
RESULT = `echo ...`
好吧,在浮点数之前是使用固定小数逻辑的时代:
IMG_WIDTH=100
IMG2_WIDTH=3
RESULT=$((${IMG_WIDTH}00/$IMG2_WIDTH))
echo "${RESULT:0:-2}.${RESULT: -2}"
33.33
最后一行是 bashim,如果不使用 bash,请尝试以下代码:
IMG_WIDTH=100
IMG2_WIDTH=3
INTEGER=$(($IMG_WIDTH/$IMG2_WIDTH))
DECIMAL=$(tail -c 3 <<< $((${IMG_WIDTH}00/$IMG2_WIDTH)))
RESULT=$INTEGER.$DECIMAL
echo $RESULT
33.33
代码背后的基本原理是:在除法之前乘以 100 得到 2 位小数。
现在是尝试 zsh 的最佳时机,zsh 是一个(几乎)bash 超集,它具有许多其他不错的功能,包括浮点数学。这是您在 zsh 中的示例:
% IMG_WIDTH=1080
% IMG2_WIDTH=640
% result=$((IMG_WIDTH*1.0/IMG2_WIDTH))
% echo $result
1.6875
这篇文章可能对您有所帮助:bash - 是否值得切换到 zsh 以供随意使用?
如果您找到了您偏好的变体,您也可以将它包装到一个函数中。
在这里,我将一些 bashism 包装到 div 函数中:
一个班轮:
function div { local _d=${3:-2}; local _n=0000000000; _n=${_n:0:$_d}; local _r=$(($1$_n/$2)); _r=${_r:0:-$_d}.${_r: -$_d}; echo $_r;}
或多行:
function div {
local _d=${3:-2}
local _n=0000000000
_n=${_n:0:$_d}
local _r=$(($1$_n/$2))
_r=${_r:0:-$_d}.${_r: -$_d}
echo $_r
}
现在你有了这个功能
div <dividend> <divisor> [<precision=2>]
并像使用它一样
> div 1 2
.50
> div 273 123 5
2.21951
> x=$(div 22 7)
> echo $x
3.14
更新 我添加了一个小脚本,它为您提供 bash 浮点数的基本操作:
用法:
> add 1.2 3.45
4.65
> sub 1000 .007
999.993
> mul 1.1 7.07
7.7770
> div 10 3
3.
> div 10 3.000
3.333
这里的脚本:
#!/bin/bash
__op() {
local z=00000000000000000000000000000000
local a1=${1%.*}
local x1=${1//./}
local n1=$((${#x1}-${#a1}))
local a2=${2%.*}
local x2=${2//./}
local n2=$((${#x2}-${#a2}))
local n=$n1
if (($n1 < $n2)); then
local n=$n2
x1=$x1${z:0:$(($n2-$n1))}
fi
if (($n1 > $n2)); then
x2=$x2${z:0:$(($n1-$n2))}
fi
if [ "$3" == "/" ]; then
x1=$x1${z:0:$n}
fi
local r=$(($x1"$3"$x2))
local l=$((${#r}-$n))
if [ "$3" == "*" ]; then
l=$(($l-$n))
fi
echo ${r:0:$l}.${r:$l}
}
add() { __op $1 $2 + ;}
sub() { __op $1 $2 - ;}
mul() { __op $1 $2 "*" ;}
div() { __op $1 $2 / ;}
它不是真正的浮点数,但如果你想要设置多个结果的东西,那么一次调用 bc ......
source /dev/stdin <<<$(bc <<< '
d='$1'*3.1415926535897932384626433832795*2
print "d=",d,"\n"
a='$1'*'$1'*3.1415926535897932384626433832795
print "a=",a,"\n"
')
echo bc radius:$1 area:$a diameter:$d
计算半径为 $1 的圆的面积和直径
在某些情况下您不能使用 bc,因为它可能根本不存在,例如在某些精简版的 busybox 或嵌入式系统中。在任何情况下,限制外部依赖总是一件好事,因此您始终可以在除以(分子)的数字上加零,这与乘以 10 的幂相同(您应该根据您需要的精度),这将使除法输出一个整数。一旦你有了那个整数,就把它当作一个字符串,并定位小数点(从右到左移动它)的次数等于你乘以分子的十次方。这是仅使用整数获得浮点结果的简单方法。
与其在命令中使用“ here strings ”(<<<
)bc
,就像最受支持的示例之一那样,这是我最喜欢的bc
浮点示例,直接来自手册页EXAMPLES
部分bc
(参见man bc
手册页)。
在我们开始之前,知道 pi 的方程是:pi = 4*atan(1)
。a()
下面是 的bc
数学函数atan()
。
这就是如何将浮点计算的结果存储到 bash 变量中的方法——在本例中存储到名为pi
. 请注意,scale=10
在这种情况下,将精度的小数位数设置为 10。此位置之后的任何十进制数字都将被截断。
pi=$(echo "scale=10; 4*a(1)" | bc -l)
现在,为了有一行代码也打印出这个变量的值,只需将echo
命令添加到末尾作为后续命令,如下所示。请注意按命令截断小数点后 10 位:
pi=$(echo "scale=10; 4*a(1)" | bc -l); echo $pi
3.1415926532
最后,让我们进行一些四舍五入。在这里,我们将使用该printf
函数四舍五入到小数点后 4 位。注意,3.14159...
现在轮到3.1416
。由于我们是四舍五入,我们不再需要使用scale=10
截断到小数点后 10 位,所以我们将删除该部分。这是最终解决方案:
pi=$(printf %.4f $(echo "4*a(1)" | bc -l)); echo $pi
3.1416
(另请参阅我的其他答案)。
请注意,从到dt_min
取整:0.01666666666...
0.017
start=$SECONDS; sleep 1; end=$SECONDS; dt_sec=$(( end - start )); dt_min=$(printf %.3f $(echo "$dt_sec/60" | bc -l)); echo "dt_sec = $dt_sec; dt_min = $dt_min"
dt_sec = 1; dt_min = 0.017
对于那些试图用接受的答案计算百分比但正在失去精度的人:
如果你运行这个:
echo "scale=2; (100/180) * 180" | bc
你99.00
只得到,这正在失去精度。
如果你这样运行它:
echo "result = (100/180) * 180; scale=2; result / 1" | bc -l
现在你得到99.99
.
因为您仅在打印时进行缩放。
参考这里
虽然您不能在 Bash 中使用浮点除法,但您可以使用定点除法。您需要做的就是将整数乘以 10 的幂,然后除掉整数部分并使用模运算得到小数部分。根据需要四舍五入。
#!/bin/bash
n=$1
d=$2
# because of rounding this should be 10^{i+1}
# where i is the number of decimal digits wanted
i=4
P=$((10**(i+1)))
Pn=$(($P / 10))
# here we 'fix' the decimal place, divide and round tward zero
t=$(($n * $P / $d + ($n < 0 ? -5 : 5)))
# then we print the number by dividing off the interger part and
# using the modulo operator (after removing the rounding digit) to get the factional part.
printf "%d.%0${i}d\n" $(($t / $P)) $(((t < 0 ? -t : t) / 10 % $Pn))
我知道它很旧,但太诱人了。所以,答案是:你不能……但你可以。让我们试试这个:
$IMG_WIDTH=1024
$IMG2_WIDTH=2048
$RATIO="$(( IMG_WIDTH / $IMG2_WIDTH )).$(( (IMG_WIDTH * 100 / IMG2_WIDTH) % 100 ))"
像这样,您在该点之后得到 2 位数字,在纯 bash 中被截断(称为四舍五入)(无需启动其他进程)。当然,如果您只需要在该点之后只需要一位数,则乘以 10 并取模 10。
这是做什么的:
$((...))
进行整数除法;$((...))
对大 100 倍的东西进行整数除法,基本上将您的 2 位数字移动到该点的左侧,然后 ( %
) 通过取模只得到这 2 位数字。奖励曲目:bc
版本 × 1000 在我的笔记本电脑上用了 1.8 秒,而纯版本用bash
了 0.016 秒。
使用计算。这是我找到的最简单的例子:
计算 1+1
2
计算 1/10
0.1
股息 = 除数 × 商 + 余数
让我们计算商和余数。以及将这些字符串连接到一个变量中。
bar=1234 \
&& divisor=1000 \
&& foo=$(printf "%s.%s" $(( bar / divisor )) $(( bar % divisor ))) \
&& printf "bar is %d miliseconds or %s seconds\n" $bar $foo
输出:bar is 1234 miliseconds or 1.234 seconds
** bash/shell 中的注入安全浮点数学运算 **
注意:此答案的重点是为在 bash(或其他 shell)中执行数学的注入安全解决方案提供想法。当然,同样可以使用,稍作调整以执行高级字符串处理等。
所提出的大多数解决方案都是使用外部数据(变量、文件、命令行、环境变量)即时构建小脚本。外部输入可用于向引擎注入恶意代码,其中很多
下面是使用各种语言执行基本数学计算的比较,其中结果为浮点数。它计算 A + B * 0.1(作为浮点数)。
所有解决方案都尝试避免创建极难维护的动态脚本,而是使用静态程序,并将参数传递给指定的变量。他们将安全地处理带有特殊字符的参数 - 减少代码注入的可能性。例外是不提供输入/输出设施的“BC”
例外是'bc',它不提供任何输入/输出,所有数据都来自标准输入中的程序,所有输出都进入标准输出。所有计算都在沙箱中执行,不允许副作用(打开文件等)。理论上,注射是设计安全的!
A=5.2
B=4.3
# Awk: Map variable into awk
# Exit 0 (or just exit) for success, non-zero for error.
#
awk -v A="$A" -v B="$B" 'BEGIN { print A + B * 0.1 ; exit 0}'
# Perl
perl -e '($A,$B) = @ARGV ; print $A + $B * 0.1' "$A" "$B"
# Python 2
python -c 'import sys ; a = float(sys.argv[1]) ; b = float(sys.argv[2]) ; print a+b*0.1' "$A" "$B"
# Python 3
python3 -c 'import sys ; a = float(sys.argv[1]) ; b = float(sys.argv[2]) ; print(a+b*0.1)' "$A" "$B"
# BC
bc <<< "scale=1 ; $A + $B * 0.1"
Bash 可以在没有任何其他程序的情况下很好地计算浮点结果。
Bash 甚至可以将π精确计算到小数点后九位。
例子:
calc=104348/33215
accuracy=9
calc99p9=$((10**$accuracy))*$calc
result99p9=$((calc99p9))
result=${result99p9: -${#result99p9}: -$accuracy}.${result99p9: -$accuracy}
echo Bash calculated pi to be $result
结果是
Bash calculated pi to be 3.141592653
正如其他人所指出的,bash 没有内置的浮点运算符。
您可以在 bash 中实现浮点,即使不使用bc和awk之类的计算器程序或任何外部程序。
我在我的项目shellmath中正是这样做的,分三个基本步骤:
作为预告,我添加了一个演示脚本,它使用以 x=0 为中心的泰勒级数计算e 。
如果你有时间,请检查一下。我欢迎您的反馈!
这是 awk 命令:-F = 字段分隔符 == +
echo "2.1+3.1" | awk -F "+" '{print ($1+$2)}'
你需要输出有多精确?如果您的用例已经可以接受通过分箱的近似值,您甚至可以更进一步并利用 POSIX 退出代码 [0:256)(所有其他整数都返回到该范围)。
例如:在 gawk/nawk/mawk-1 中,它已经让我的纪元秒数下降到整数级别,但我想扩展它以提取接近毫秒的精度,但又不过分迂腐,我在 POSIX shell 中运行此命令
exit $(( 10#` gdate +%5N ` * 256 / 100000 ))
直接将表示 gnu-date 输出的 5 位整数分配到 256 个 bin 中的 1 个,然后在awk 获得调用的退出代码(即所选 bin # 0.xxxxxx
)后取消该操作。我发现这种方法的开销比使用完整的 getline 调用要低。256
system()
此方法还直接将输出捕获到 POSIX 退出代码中,而不需要额外的终端打印输出。
auto floors it to integer
(如果以这种方式编写而不是 * 0.0256,则为shell 算术)。放在一个 awk 函数中,它会类似于这样。这10#
是强制 base-10 以防止 posix shell 将“01733”解释为八进制数。
function msecs() { # n x 2**-8 = n divided by 256
return 2^-8 * \
system( "exit \44\50\50 " \
" 10\43\140 gdate \53" \
"%5N\140 \52 " \
"256 \57 100000 \51\51" )
}