Bash 的算术运算
Bash 的算术运算
可通过 declare -i
声明整数变量,具体请看《Bash 变量》的 declare 命令
小节
算术表达式
((...))
语法可以进行整数的算术运算。((...))
会自动忽略内部的空格,所以下面的写法都正确,得到同样的结果。不过算术表达式只能计算整数,否则会报错。
$ ((age = 12 -2 ))
$ echo $age
10
$ ((2+2))
$ (( 2+2 ))
$ (( 2 + 2 ))
只能计算整数,不能计算小数
$ ((2.5 -1))
-bash: ((: 2.5 -1: syntax error: invalid arithmetic operator (error token is ".5 -1")
算数表达式不返回值,表达式地执行的结果根据算术运算的结果而定。只要算术结果不是 0
,表达式就算执行成功,即 $?
为 0。如果表达式结果为 0
,则表示执行失败,$?
就不为 0
,一般都会为 1
$ ((age = 2+2))
$ echo $?
0
$ ((age = 2-2))
$ echo $?
1
如果要读取算术运算的结果,需要在 ((...))
前面加上美元符号 $((...))
,使其变成算术表达式,返回算术运算的值。
我们在《Bash 的模式拓展》中的
算术扩展
小节已经接触过
$ echo $(( 1 + 2 +2 ))
5
((...))
语法支持的算术运算符如下。
+
:加法-
:减法*
:乘法/
:除法(整除):除法运算符的返回结果总是整数,比如5
除以2
,得到的结果是2
,而不是2.5
%
:余数:比如5
除以2
,余数是1
**
:指数++
:自增运算(前缀或后缀):++i
表示先加后用,i++
表示先用后加--
:自减运算(前缀或后缀):与上同理
((...))
内部还支持 ()
的嵌套,就像写数学表达式一样,$((...))
结构可以嵌套。
$ ((val = ((5+5)*10 -20)/5 ))
$ echo $val
16
自增
$ val=0
$ echo $((val++))
0
$ echo $val
1
$((...))
的圆括号之中,不需要在变量名之前加上 $
,不过加上也不报错。
如果在 $((...))
里面使用带引号的字符串,会报错,
在大佬的 博客 中,
$((...))
会将带引号的字符串认作是一个变量名。跟我的实际情况不一样。
$ echo $(( "hello" + 2))
-bash: "hello" + 2: syntax error: operand expected (error token is ""hello" + 2")
如果 $((...))
里面使用不存在的变量,或者变量的值不为整数,也会当作 0
处理。
$ echo $((name + 12))
22
如果变量的值为整数,则会生效
$ name=xiashuo
$ echo $((name + 12))
12
$ name=22
$ echo $((name + 12))
34
$ name=28
$ echo $((name + 12))
40
最后,$[...]
是以前的语法,也可以做整数运算,不建议使用。
$ echo $[2+2]
4
数值的进制
Bash 的数值默认都是十进制,但是在算术表达式中,也可以使用其他进制。
number
:没有任何特殊表示法的数字是十进制数(以 10 为底)。0number
:八进制数。0xnumber
:十六进制数。base#number
:base
进制的数。
不同进制的数,在算数表达式中可以进行运算,最终通过 echo 输出的时候的时候,都会转化为十进制数,非常方便。
简单实践:
$ echo $((10))
10
$ echo $((010))
8
$ echo $((0x10))
16
$ echo $((3#10))
3
$ echo $((2#10101010))
170
$ echo $((4#101))
17
# 3 进制数 加上一个 2 进制数
$ echo $((3#10 + 2#101))
8
# 4 进制数 加上一个 2 进制数
$ echo $((4#11 + 2#11))
8
位运算
$((...))
支持以下的二进制位运算符。
<<
:位左移运算,把一个数字的所有位向左移动指定的位。>>
:位右移运算,把一个数字的所有位向右移动指定的位。&
:位的“与”运算,对两个数字的所有位执行一个AND
操作。|
:位的“或”运算,对两个数字的所有位执行一个OR
操作。~
:位的“否”运算,对一个数字的所有位取反。^
:位的异或运算(exclusive or),对两个数字的所有位执行一个异或操作。
下面是二进制数位运算的的例子。
$ echo $((2#1111))
15
$ echo $((2#1111 >> 2))
3
$ echo $((2#1111 << 2))
60
$ echo $((2#11 << 2))
12
$ echo $((2#1111 & 2#1100))
12
$ echo $((2#1111 | 2#1100))
15
$ echo $((~2#1100))
-13
$ echo $((2#1111 ^ 2#1100))
3
这里解释一下为什么 $((~2#1100))
为 -13
,这里涉及到计算机中二进制存储相关的知识,在计算机中所有的数字都是以二进制补码的形式进行存储和表示的,也是以补码的方式进行计算,那 2#1100
也是补码,按位取反之后就是 2#0011
,这就是补码,那这个补码对应的二进制数怎么求呢?先计算其反码 0010
,然后获取原码 1101
,然后就是 13,然后因为之前符号位是 0,现在符号位取反,就是 1,所以为负数,就是 -13,所以最后输出 -13
关于原码、反码、补码的知识,请看《Java 核心技术卷一 _ 第 3 章 _Java 基本程序设计结构》的
原码、反码、补码
小节。
也可以对十进制数进行位运算,不过看起来就不明显了
$ echo $((7 << 2))
28
$ echo $((7 >> 2))
1
7 的补码和源码相同,是 111
逻辑运算
$((...))
支持以下的逻辑运算符。
<
:小于>
:大于<=
:小于或相等>=
:大于或相等==
:相等!=
:不相等&&
:逻辑与||
:逻辑或!
:逻辑否expr1?expr2:expr3
:三元条件运算符。若表达式expr1
的计算结果为非零值(算术真),则执行表达式expr2
,否则执行表达式expr3
。
如果逻辑表达式为真,返回 1
,否则返回 0
。
我们在《Bash 条件判断》小节中会使用
test
命令进行条件判断,但是写起来比较麻烦,比如-gt
、-lt
,如果是简单的算数运算的话,直接使用算数表达式直接使用>
或者<
会比较方便。
简单实践:
$ name=xiashuo
$ echo $(( ${#name} > 4 ))
1
$ echo $(( ${#name} > 10 ))
0
$ echo $(( 1 < 3 || 5>2 ))
1
$ echo $(( 1 < 3? 111 :2222 ))
111
上面例子中,第一个表达式为真时,返回第二个表达式的值,否则返回第三个表达式的值。
赋值运算
算术表达式 $((...))
可以执行赋值运算。
$ echo $((a=3))
3
$ echo $a
3
上面例子中,a=3
对变量 a
进行赋值。这个式子本身也是一个表达式,返回值就是等号右边的值。
$((...))
支持的赋值运算符,有以下这些。
parameter = value
:简单赋值。parameter += value
:等价于parameter = parameter + value
。parameter -= value
:等价于parameter = parameter – value
。parameter *= value
:等价于parameter = parameter * value
。parameter /= value
:等价于parameter = parameter / value
。parameter %= value
:等价于parameter = parameter % value
。parameter <<= value
:等价于parameter = parameter << value
。parameter >>= value
:等价于parameter = parameter >> value
。parameter &= value
:等价于parameter = parameter & value
。parameter |= value
:等价于parameter = parameter | value
。parameter ^= value
:等价于parameter = parameter ^ value
。
基本上就是支持算术运算和位运算的赋值运算,使用起来还是很方便的
$ val=7
$ echo $((val <<= 2))
28
$ echo $((val *= 2))
56
如果在表达式内部赋值,可以放在圆括号中,否则会报错。
$ echo $(( a<1 ? (a+=1) : (a-=1) ))
求值运算
逗号 ,
在 $((...))
内部是求值运算符,执行前后两个表达式,并返回后一个表达式的值。也就是说,通过在 $((...))
中使用 ,
,可以将多个表达式写在一个 $((...))
内部,并返回最终的计算结果
$ val=7
$ echo $((val <<= 2,val *=2))
56
expr 命令
expr
命令支持算术运算,可以不使用 ((...))
语法。注意符号跟数字之间必须有空格,否则会当成字符串直接输出
总体来说,使用体验不如算数运算符
$ expr 3+2
3+2
$ expr 3 + 2
5
expr
命令支持变量替换。
$ foo=3
$ expr $foo + 2
5
expr
命令也不支持非整数参数。
$ expr 3.5 + 2
expr: non-integer argument
上面例子中,如果有非整数的运算,expr
命令就报错了。
let 命令
let
命令用于将算术运算的结果,赋予一个变量。
$ let x=2+3
$ echo $x
5
上面例子中,变量 x
等于 2+3
的运算结果。
注意,x=2+3
这个式子里面不能有空格,否则会报错。
let
命令的详细用法参见《Bash 变量》。