shell 数学运算
数学运算
对 shell 脚本来说,处理运算的过程比较麻烦,而且 shell 对整数和浮点数的处理方式也不同,因此下面将整数和浮点操作分开讲述。
Bash 支持一系列的算数运算,列表如下,它们可以工作在 let, declare, 算术扩展(arithmetic expansion)这三种方式之下。
Arithmetic Operator | Description | ||
---|---|---|---|
id++, id-- | variable post-increment, post-decrement | ||
++id, --id | variable pre-increment, pre-decrement | ||
-, + | unary minus, plus | ||
!, ~ | logical and bitwise negation | ||
** | exponentiation | ||
*, /, % | multiplication, division, remainder (modulo) | ||
+, - | addition, subtraction | ||
<<, >> | left and right bitwise shifts | ||
<=, >=, <, > | comparison | ||
==, != | equality, inequality | ||
& | bitwise AND | ||
^ | bitwise XOR | ||
bitwise OR | |||
&& | logical AND | ||
logical OR | |||
expression ? expression : expression | conditional operator | ||
=, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, | = | assignment |
整数运算
推荐使用第三种方法进行整数运算
legacy way: expr
最开始, Bourne shell 提供了外置的命令行工具
expr
来在命令行上处理整数运算,但是比较笨拙,有些运算符号与 shell 的元字符冲突,需要转义(例如:expr 1 \* 5
),而且还有格式要求,因此虽然现在这个命令还存在,但是我们不经常使用它了。bash shell 为了保持跟 Bourne shell 的兼容而包含了 expr 命令,但它同样也提供了一种更简单的方法来执行数学表达式。
shell builtin commands:let or declare
let
命令运算符与操作数之间不能有空格1
2
3
4
5
6
7
8
9
10violetv at manjaro in ~ [15:23:42]
var=2
violetv at manjaro in ~ [15:23:52]
let var+=3
violetv at manjaro in ~ [15:23:56]
echo $var
5
let x=4 y=5 z=x*y u=z/2declare
进行整数运算还要写-i
选项Integer Declaration using the declare -i notation can lead to confusing or hard to read shell scripts.
容易混淆或者降低代码可读性,因此放弃治疗。
(推荐) 算数扩展
在 Bash 及更高级 shell 中求整数算术表达式的值的推荐方法是使用 shell 的算术扩展(arithmetic expressions)。 内置的 shell 扩展允许你使用
((expression))
来进行整数运算,支持上文的表格中的所有操作符号。格式:
$((...))
,其中运算数,运算符和括号之间有无空格都可(舒适~)。$((...))
被称为算数扩展(Arithmetic Expansion ),(())
被称为复合命令(compound command ),在 bash 中用来计算一个算数扩展。注意:
$[...]
方括号也能在 bash 中做算数扩展,但是该方法被弃用了,目前推荐使用$(())
。 注意,不需要将双括号中表达式里的大于号转义。这是双括号命令提供的另一个高级特性。使用示例:
1
2
3
4
5
6
7
8
9
10
11var1=3 && echo $var1
3
((var1 += 3)) && echo $var
6
var1=$((var1 + 4)) && echo $var1
10
echo $((var1++))
10
echo $var1 && echo $((++var1))
11
12
进制运算
格式:base#number
示例:
1 | echo $(( 16#A+2 )) |
浮点数运算
可以发现例如
$(( 3/2 ))
的算数扩展的返回值都是整数,而不是浮点数,因此,当需要得到浮点数时,我们需要进一步处理。
使用 printf 内置命令
公式:
printf %.<precision>f "$((10**<multiplier> * 2/3))e-<multiplier>
注意:%.<precision>f
里面的精度不要比 multiplier
自身大,否则会填充数字零。
示例:
1 | printf "结果为: %.3f\n" "$(( 10**3 * 2/3))e-3" |
awk 命令
1 | awk "BEGIN {print 100/3}" |
使用负数时,注意负号前面要有空格:
awk "BEGIN {print -8.4 - -8}"
bc 命令
bc - An arbitrary precision calculator language
浮点运算是由内建变量
scale
控制的。必须将这个值设置为你希望在计算结果中保留的小数位数,否则无法得到期望的结果。scale 变量的默认值是 0。在 scale 值被设置前,bash 计算器的计算结果不包含小数位。在将其值设置成 4 后,bash 计算器显示的结果包含四位小数
-l
默认保留 20 位小数
1 | echo "1.6+3/2"|bc |
使用示例:
1 | echo "15.6+299.33*2.3/7.4" | bc |
保留指定小数位:
1 | echo "scale=2; 2/3" | bc |
注意: echo 和 bc 保留指定小数的方法有些不精确, echo 有时直接截断,而不实施四舍五入,因此,建议使用的 printf 或者是 awk. 案例:
当你需要进行大量运算,在一个命令行中列出多个表达式就会有点麻烦,可以使用 here document(内联重定向)。
1 | variable=$(bc << EOF |
注:将选项和表达式放在脚本的不同行中可以让处理过程变得更清晰,提高易读性
参考资料
- Math Arithmetic: How To Do Calculation in Bash?
- Linux 命令行与 Shell 脚本编程大全