算术运算
Shell允许在某些情况下对算术表达式进行求值,比如:let和declare 内置命令,(( ))复合命令和算术扩 展。求值以固定宽度的整数进行,不检查溢出,尽管除以0 被困并标记为错误。运算符及其优先级,关 联性和值与C语言相同。以下运算符列表分组为等优先级运算符级别。级别按降序排列优先。
注意:bash 只支持整数,不支持小数
* / % multiplication, division, remainder, %表示取模,即取余数,示例:9%4=1,5%3=2 + - addition, subtraction
i++ i-- variable post-increment and post-decrement
++i --i variable pre-increment and pre-decrement
= *= /= %= += -= <<= >>= &= ^= |= -+ unary minus and plus
!~ logical and bitwise negation ** exponentiation 乘方,即指数运算 << >> left and right bitwise shifts <= >= < > comparison
== != equality and inequality
& bitwise AND
| bitwise OR
^ bitwise exclusive OR
&& logical AND
|| logical OR
expr?expr:expr conditional operator
expr1 , expr2 comma
乘法符号有些场景中需要转义
实现算术运算的方法
(1) let var=算术运算表达式
(2) ((var=算术运算表达式)) 和上面等价
(3) var=$[算术运算表达式]
(4) var=$((算术运算表达式))
(5) var=$( expr arg1 arg2 arg3 ... ) # 注意两边的空格
(6) declare -i var = 数值
(7) echo '算术表达式' | bc
内建的随机数生成器变量:
$RANDOM 取值范围:0-32767
示例
# 生成 0 - 49 之间的随机数
[root@centos8 ~]#echo $[$RANDOM%50]
49
# 随机字体颜色
[root@centos8 ~]#echo -e "\033[1;$[RANDOM%7+31]mhello\033[0m"
hello
[root@centos8 ~]#echo -e "\033[1;$[RANDOM%7+31]mhello\033[0m"
hello
[root@centos8 ~]#echo -e "\033[1;$[RANDOM%7+31]mhello\033[0m"
hello
[root@centos8 ~]#
增强型赋值:
+= i+=10 # 相当于 i=i+10
-= i-=j # 相当于 i=i-j
*=
/=
%=
++ i++ ++i # 相当于 i=i+1
-- i-- --i # 相当于 i=i-1
1. (())用法(常见且效率最高)
执行简单的整数运算,只需要将特定的算术表达式用"$(("和"))"括起来
shell的算术运算符号都置于"$((".........."))"语法中,这一语法如同上双引号功能,除了内嵌双引号无需转义
注意:
-
$() 代表包含的是系统命令
-
$(()) 代表包含的是数值运算
变量表达式定义:((a=1+2**3-4%3))
或者 b=$((a=1+2**3-4%3))
[root@centos ~]#((a=1+2**3-4%3))
[root@centos ~]#echo $a
8
[root@centos ~]#b=$((a=1+2**3-4%3))
[root@centos ~]#echo $b
8
[root@centos ~]#echo $((1+2**3-4%3))
8
[root@centos ~]#
echo $((a+=1)) 相当于echo $((a=a+1))
echo $((3>2))
echo $((3<2))
echo $((100*(100+1)/2))
# 变量定义计算
myvar=99
echo $(($myvar + 1))
# 或者
myvar=$(( $myvar+1 ))
echo $myvar
[root@centos ~]#a=10
[root@centos ~]#echo $((a++)) # 先输出a自身的值,因为a为10,所以输出10
10
[root@centos ~]#echo $a # 上面输出a的值后,a自增1,所以a为11
11
[root@centos ~]#a=10
[root@centos ~]#echo $((++a))
11
[root@centos ~]#echo $a
11
[root@centos ~]#
[root@centos ~]#a=10
[root@centos ~]#echo $((a--)) # 先输出a自身的值,因为a为10,所以输出10
10
[root@centos ~]#echo $a # 上面输出a的值后,a自减1,所以a为9
9
[root@centos ~]#a=10
[root@centos ~]#echo $((--a))
9
[root@centos ~]#echo $a
9
[root@centos ~]#
结论
echo $((a++))和echo $((a--)) # 表示先输出a自身的值,然后再进行++ --的运算
echo $((++a))和echo $((--a)) # 表示先进行++ -- 的运算,再输出a自身的值
变量在前,先输出变量值,变量在后,就是先运算后输出变量的值
命令行传参方式:
#/bin/bash
if [ $# -ne 2 ];then
echo "Usage:$0 NUM1 NUM2"
exit 1
fi
[ -n "$(echo $1|sed 's#[0-9]##g')" ] && {
echo "num1 must be int"
exit 1
}
[ -n "$(echo $2|sed 's#[0-9]##g')" ] && {
echo "num2 must be int"
exit 1
}
a=$1
b=$2
echo "a-b = $(( $a - $b ))"
echo "a+b = $(( $a + $b ))"
echo "a*b = $(( $a * $b ))"
echo "a/b = $(( $a / $b ))"
echo "a**b =$(( $a ** $b ))"
echo "a%b = $(( $a % $b ))"
简单实现一个加减乘除功能的计算器,通过命令行传参的方式实现
方法一: echo $(($1))
方法二: echo $(($1$2$3))
2. let赋值表达式
let赋值表达式功能等同于:((赋值表达式)),let命令尽量不用
[root@centos ~]#i=2
[root@centos ~]#let i=i+8
[root@centos ~]#echo $i
10
[root@centos ~]#
# 提示:let i=i+8等同于((i=i+8)),但后者效率更高
++i:先自加,再返回 i
#!/bin/bash
#
i=100
let j=++i # 此处先会返回 i=i+1 的值,即 i=100+1=101,然后再赋值给 j ,所以 j 也等于 101
echo $i # i=101
echo $j # j=i=101
i++:先返回 i ,再自加
#!/bin/bash
#
i=100
let j=i++ # 此处会先返回 i 的值,即 i=100,然后再赋值给 j ,所以 j 等于 100,最后再返回 i=i+1,即i=100+1=101,所以,此时 i=101
echo $j # j=100
echo $i # i=101
3. expr命令用法
expr命令一般用于整数值,但也可用于字符串,用来求表达式变量的值,同时expr也是一个手工命令行计算机
1、注意运算符和数字左右都有空格,否则不会运算
[@sjs_115_196 ~]# expr 11 + 1>/dev/null
expr: syntax error
[@sjs_115_196 ~]# echo $?
2
[@sjs_115_196 ~]# expr 11 + 1 >/dev/null
[@sjs_115_196 ~]# echo $?
0
[@sjs_115_196 ~]#
2、使用乘号时,必须用反斜线屏蔽其特定含义,因为shell可能会误解星号的含义
[root@centos ~]#expr 2 * 2
expr: syntax error: unexpected argument 'anaconda-ks.cfg'
[root@centos ~]#expr 2 \* 2
4
[root@centos ~]#
3、增量计算:
expr在循环中可用于增量计算,首先,循环初始化为0,然后循环加1,反引号的用法为命令替代
最基本的一种是从(expr)命令接受输出并将之放入循环变量
i=0
i=$(expr $i + 1)
echo $i
(2)expr $[$a+$b] 表达式形式:
expr $[2+3]
expr $[2*3]
expr $[2**3]
expr $[2/3]
(3) expr和变量的子串截取简单对比
expr length "I am songwanbo"
expr substr "I am songwanbo" 2 2
# 等同于
var="I am songwanbo"
echo ${#var} # 计算字符串的长度
echo ${var:2:2} # 从第2个开始取,取2个
示例:变量的处理,计算变量长度与其他不同方法的耗时对比
chars=`seq -s " " 100`
$ echo ${#chars} # 速度最快
$ echo $(expr length "$chars") 或者 expr length "$chars"
$ echo ${chars}|wc -L
time for i in $(seq 10000);do count=${#chars};done # 速度最快
time for i in $(seq 10000);do count=`expr length "$chars"`;done # 介于两者之间
time for i in $(seq 10000);do count=`echo ${chars}|wc -L`;done # 速度最慢
4. 其他
[root@centos8 ~]#echo $[2+3]
5
[root@centos8 ~]#echo $[ 2 * 3 ]
6
[root@centos8 ~]#typeset -i A=1 B=3
[root@centos8 ~]#A=A+B
[root@centos8 ~]#echo $A
4
[root@centos8 ~]#
5. bc是unix下的计算器,它可以用在命令行下面
i=2
i=`echo $i + 1|bc` 效率低
因为bc支持科学计算,所以这种方法功能非常强大
echo "touch file"
echo "touch file"|bash
echo 1+1|bc
echo 3.5+5.6|bc
echo "5.23*3.32"|bc -l
echo "obase=2;8"|bc 将10进制8转换成2进制
echo "obase=16:20"|bc 将10进制20转换成16进制
echo "scale=6;5.23*3.12"|bc 结果取6位
计算1+2+3+...+100,用bc计算,拼接1+2+3+4+5+6+....100
# 方法1:
seq -s "+" 100|bc # -s 指定分隔符
# 方法2:
echo {1..100}|tr " " "+"|bc # 用tr做替换,把空格替换成+号
# for循环的写法:
#!/bin/bash
num=0
for i in `seq 100`
do
num=$(($num+$i))
done
echo $num
# while循环的写法:
#!/bin/bash
num=0
i=0
while [ $i -le 100 ]
do
num=$(($num+$i))
i=$(($i+1))
done
echo $num
6. 示例
数值运算
# 方法一:declare
[root@centos ~]#x=111
[root@centos ~]#y=222
[root@centos ~]#declare -i z=$x+$y
[root@centos ~]#echo $z
333
[root@centos ~]#
# 方法二:expr或let数值运算工具
[root@centos ~]#x=111
[root@centos ~]#y=222
[root@centos ~]#z=$(expr $x + $y) # 注意,+号左右两则必须有空格
[root@centos ~]#echo $z
333
[root@centos ~]#
# 方法三:$((运算式)) 或 $[运算式]
[root@centos ~]#x=111
[root@centos ~]#y=222
[root@centos ~]#z=$(( $x + $y ))
[root@centos ~]#echo $z
333
[root@centos ~]#zz=$[ $x + $y ]
[root@centos ~]#echo $zz
333
[root@centos ~]#
虽然乘法和除法的优先级高于加法,但是通过小括号可以调整运算优先级
[root@centos ~]#sum=$(( (11+3)*3/2 ))
[root@centos ~]#echo $sum
21
[root@centos ~]#
14不能被3整除,余数是2
[root@centos ~]#s=$(( 14%3 ))
[root@centos ~]#echo $s
2
[root@centos ~]#
逻辑与运算只有相与的两边都是1,与的结果才是1,否则与的结果是0
[root@centos ~]#s=$(( 1 && 0 ))
[root@centos ~]#echo $s
0
[root@centos ~]#
范例:今有雉兔同笼,上有三十五头,下有九十四足,问雉兔各几何?
鸡兔同笼,是中国古代著名典型趣题之一,记载于《孙子算经》之中。
[root@centos8 scripts]#cat chook_rabbit.sh
#!/bin/bash
#
HEAD=$1
FOOT=$2
RABBIT=$(((FOOT-HEAD-HEAD)/2))
CHOOK=$[HEAD-RABBIT]
echo RABBIT:$RABBIT
echo CHOOK:$CHOOK
[root@centos8 scripts]#./chook_rabbit.sh 30 80
RABBIT:10
CHOOK:20
逻辑运算
true, false
1,真
0,假
# 注意:以上为二进制
与:&:和0相与结果为0,和1相与结果保留原值, 一假则假,全真才真
0 与 0 = 0
0 与 1 = 0
1 与 0 = 0
1 与 1 = 1
与逻辑
A | B | F |
---|---|---|
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
示例
[root@centos8 ~]#x=$[2&6]
[root@centos8 ~]#echo $x
2
[root@centos8 ~]#x=$[7&3]
[root@centos8 ~]#echo $x
3
或:|:和1相或结果为1,和0相或结果保留原值,一真则真,全假才假
0 或 0 = 0
0 或 1 = 1
1 或 0 = 1
1 或 1 = 1
或逻辑
A | B | F |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
非:!
! 1 = 0 ! true
! 0 = 1 ! false
非逻辑
A | F |
---|---|
0 | 1 |
1 | 0 |
异或:^
异或的两个值,相同为假,不同为真。两个数字X,Y异或得到结果Z,Z再和任意两者之一X异或,将得出另一个值Y
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0
示例
[root@centos8 ~]#true
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#false
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#! true
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#! false
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#
示例:变量互换
# 传统写法
[root@centos8 ~]#x=10;y=20;temp=$x;x=$y;y=$temp;echo x=$x,y=$y
x=20,y=10
# 异或写法
[root@centos8 ~]#x=10;y=20;x=$[x^y];y=$[x^y];x=$[x^y];echo x=$x,y=$y
x=20,y=10
短路运算
- 短路与 &&
CMD1 短路与 CMD2
第一个CMD1结果为真 (1),第二个CMD2必须要参与运算,才能得到最终的结果
第一个CMD1结果为假 (0),总的结果必定为0,因此不需要执行CMD2
- 短路或 ||
CMD1 短路或 CMD2
第一个CMD1结果为真 (1),总的结果必定为1,因此不需要执行CMD2
第一个CMD1结果为假 (0),第二个CMD2 必须要参与运算,才能得到最终的结果