shell算数和逻辑运算

时间:2023-12-09 18:36:55

算术运算

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 必须要参与运算,才能得到最终的结果