数值和运算

Bash 支持整数和浮点数,并且支持基本的数学运算。内置的命令 exprlet(())只支持整数运算。至于浮点数运算可以使用 bc。本文后续将bc介绍浮点数运算。 随着Bash版本的演进,现在推荐使用(())进行算术运算。这里主要介绍(())letexpr也会简单描述一下

表达式

  1. 使用((...))语法结构进行整数运算,((...))支持C语言风格的算术表达式。 ((...))内会忽略空格,字符串会被当做变量引用变量处理,$可以省略
  2. ((...))可以理解为命令,其并不返回计算结果,((...))命令执行结果由算术表达式的计算结果决定,计算结果不为0时,((...))命令执行成功,为0时,((...))命令执行失败。
  3. 若要获取计算结果可以使用$((...))$((...))不是命令,不能单独执行,只能被其他命令引用。
#!/bin/bash

((sum=1+2+3+4+5))
echo $? # 输出0,命令执行成功
echo $sum # 输出10

# 直接执行
echo $((1+2+3+4+5))

males=20
females=30
total=$((males+females)) # 变量males和females的值被使用,并且省略了$
echo $total
echo $((total = males + females + 1)) # 输出51

# (()) 在处理字符串会递归下去,直到找到相应的变量,或者变量不存在
males=temp
temp=11
echo $((total = males + females + 1)) # 输出42

echo $((1.0 + 2)) # 语法错误
echo $(("jartap" + 2)) # 在Bash不同版本中执行情况可能不同,4.2版本执行错误,4.4版本输出2

((...))中可以使用()调整优先级,((...))支持的运算符有:

  1. +:加法
  2. -:减法
  3. *:乘法
  4. /:除法,整除:抹除小数部分,例如余数是5.5,则结果为5
  5. %:余数
  6. **:指数
  7. ++:自增运算,可以为前缀或后缀
  8. –:自减运算,可以为前缀或后缀

优先级:

  1. 括号:首先计算括号内的表达式。
  2. 自增、自减:自增和自减的优先级高于指数运算。
  3. 指数(**):优先级高于加减乘除,但是优先级低于括号。
  4. 乘法和除法(*, /,%):从左到右计算乘法和除法,它们具有相同的优先级。
  5. 加法和减法(+, -):然后从左到右计算加法和减法,它们也具有相同的优先级。

数值常量

在变量章节中,我们知道Bash变量是没有类型的,其都是字符串;但是Bash会根据字符串的内容,自动识别其类型,比如数字字符串会被识别为数字类型,而字母字符串会被识别为字符串类型。Bash中,数字类型有四种:1. 十进制数字;2. 十六进制数字;3. 八进制数字。 默认情况下数字类型是十进制数字,其基本形式如下:

  1. num,默认十进制数
  2. 0xnum,十六进制数
  3. 0num,八进制数
  4. base#num,base进制的数据,num中的数字不能超过base进制的表示范围,例如可以用2#1010表示二进制数(1010),其十进制表示为10

我们来看些示例:

echo $((0x41)) # 输出:65,等价于$((16#41))
echo $((2#1000001)) # 输出:65
a=10
b=0xA
echo "$a"  # 10
echo "$b"  # 0xA

echo $((a+b)) # 20

位运算

(())还支持位运算符,位运算符一般低于算术运算符:

  1. <<:位左移运算,把一个数字的所有位向左移动指定的位。
  2. >>:位右移运算,把一个数字的所有位向右移动指定的位。
  3. &:位的“与”运算,对两个数字的所有位执行一个AND操作。
  4. |:位的“或”运算,对两个数字的所有位执行一个OR操作。
  5. ~:位的“否”运算,对一个数字的所有位取反。
  6. ^:位的异或运算(exclusive or),对两个数字的所有位执行一个异或操作。

看一些示例:

#!/bin/bash  

# ------------------------<<位左移运算-------------------------------------
a=5  # 二进制表示为 0101  
((b = a << 2))  # 左移2位  
echo $b  # 输出:20,二进制表示为 10100

# ------------------------>>位右移运算-------------------------------------
c=20  # 二进制表示为 10100  
((d = c >> 2))  # 右移2位  
echo $d  # 输出:5,二进制表示为 0101

# ------------------------&位与运算----------------------------------------
# 对 6(二进制 110)和 3(二进制 011)执行位与操作  
result=$((6 & 3))  
echo "6 & 3 = $result"  # 输出: 6 & 3 = 2

# ------------------------&位或运算----------------------------------------
# 对 6(二进制 110)和 3(二进制 011)执行位或操作  
result=$((6 | 3))  
echo "6 | 3 = $result"  # 输出: 6 | 3 = 7

# ------------------------&位否运算----------------------------------------
# 对 5(二进制 0101)执行位否操作,注意 Bash 使用 32 位整数  
# 因此,结果将是 -6,因为在补码表示中,对 0101 取反得到 1010...(前面还有很多 1)  
# 然后加 1 变成补码形式的 -6  
result=$((~5))  
echo "~5 = $result"  # 输出可能是: ~5 = -6,具体取决于系统如何处理整数

# ------------------------&位或运算----------------------------------------
# 对 6(二进制 110)和 3(二进制 011)执行位异或操作  
result=$((6 ^ 3))  
echo "6 ^ 3 = $result"  # 输出: 6 ^ 3 = 5

赋值运算

(())可以进行赋值运算,如:

((a=1+1))
echo $a # 输出:2

# 当使用
echo $((a=1+1)) # 输出:2,$(())会返回最后一个表达式的值作为返回值,多个表达式之间用逗号分隔
echo $((a=1+2, b=2+2)) # 输出:4, 甚至可以直接分隔数字
echo $a,$b # 输出:3,4
echo $((1, 2)) # 输出:2

逻辑运算

(())支持逻辑运算符:

  1. <:小于
  2. >:大于
  3. <=:小于或相等
  4. >=:大于或相等
  5. ==:相等
  6. !=:不相等
  7. &&:逻辑与
  8. ||:逻辑或
  9. !:逻辑否
  10. expr1?expr2:expr3:三元条件运算符。若表达式expr1的计算结果为非零值(算术真),则执行表达式expr2,否则执行表达式expr3。

let 命令

let命令类似,(( ... )) 结构允许对算术表达式的扩展和求值。它是let命令的简化形式。例如,a=$(( 5 + 3 )) 会将变量a赋值成 5 + 3,也就是8。在Bash中,双圆括号结构也允许以C风格的方式操作变量。例如,(( var++ ))

expr 命令

expr: evalute(求值)expressions(表达式))命令既可以用于整数运算,也可以用于相关字符串长度、匹配等运算处理

bc 命令