四、Shell脚本语言的运算
4.1 算数运算
shell支持算术运算,但只支持整数,不支持小数
4.2 Bash中的算术运算
-- + 加法运算 -- - 减法运算 -- * 乘法运算 -- / 除法运算 -- % 取模,即取余数 -- ** 乘方 #乘法符号在有些场景需要转义
4.2 实现算术运算
1. let var=算术表达式
2. var=$[算术表达式]
3. var=$((算术表达式))
4. var=$(expr arg1 arg2 arg3 ...)
5. declare -i var = 数值
6. echo '算术表达式' | bc (支持浮点数)
4.3 增强型赋值:
+= i+=10 <==> i=1+10 -= i-=j <==> i=i-j *= /= %= ++ i++,++i <==> i=i+1 (自增) -- i--,--i <==> i=i-1 (自减)
实例:
[root@localhost ~]# let var+=1
[root@localhost ~]# echo $var
1
[root@localhost ~]# let var++
[root@localhost ~]# echo $var
2
[root@localhost ~]# let var-=1
[root@localhost ~]# echo $var
1
[root@localhost ~]# let var--
[root@localhost ~]# echo $var
0
# i++ 与 ++i的区别:
i++ 先赋值再运算
++i 先运算再赋值
[root@localhost ~]# unset i j ;i=1;let j=i++;echo "i=$i,j=$j"
i=2,j=1
[root@localhost ~]# unset i j ;i=1;let j=++i;echo "i=$i,j=$j"
i=2,j=2
[root@localhost ~]#
实例:鸡兔同笼问题
(1)
[root@localhost ~]# vim xiaojiji.sh
#!/bin/bash
HEAD=35
FOOT=94
RABBIT=$(((FOOT-HEAD-HEAD)/2))
CHICKEN=$[35-RABBIT]
echo "兔子的数量为:"$RABBIT
echo "鸡的数量为:"$CHICKEN
[root@localhost ~]# chmod +x xiaojiji.sh
[root@localhost ~]# ./xiaojiji.sh
兔子的数量为:12
鸡的数量为:23
(2)在脚本中写入变量,让用户在命令行写入需要计算的数值
[root@localhost ~]# vim xiaojiji.sh
#!/bin/bash
HEAD=$1
FOOT=$2
RABBIT=$(((FOOT-HEAD-HEAD)/2))
CHICKEN=$[35-RABBIT]
echo "兔子的数量为:"$RABBIT
echo "鸡的数量为:"$CHICKEN
[root@localhost ~]# ./xiaojiji.sh 30 80
兔子的数量为:10
鸡的数量为:25
4.2 逻辑运算(了解,不用掌握)
True用数字表示1,False用数字表示0
与:&
1 与 1 = 1 1 与 0 = 0 0 与 1 = 0 0 与 0 = 0
或:|
1 或 1 = 1 1 或 0 = 1 0 或 1 = 1 0 或 0 = 0
非:!
!1 = 0 !True=False !0 = 1 !False=True
异或:^
#异或的两个值,相同为假,不同为真 1 ^ 1 =0 1 ^ 0 =1 0 ^ 1 =1 0 ^ 0 =0
条件表达式
条件测试命令
条件测试:判断某需求是否满足,需要由测试机制来实现,专用的测试表达式需要由测试命令辅助完成测试过程,实现评估布尔声明,以便在条件性环境下进行执行。
若真,则状态码变量$?返回0
若假,则状态码变量$?返回1
扩展: [ ] 与 [[ ]] 的区别
区别1:
[ ]是符合POSIX标准的测试语句,兼容性强,几乎可以运行在所有的Shell解释器中
[[ ]]仅可运行在特定的几个Shell解释器中(如Bash)
区别2:
[[ ]] 里面支持数学类型的操作数。例如:< >。但不支持 =号。
[root@localhost ~]# [[ 2 >= 1 ]]
-bash: 条件表达式中有语法错误
[root@localhost ~]# [[ 2 = 1 ]]
-bash: 未预期的记号 "2" 附近有语法错误
[root@localhost ~]# [[ 2 > 1 ]]
[root@localhost ~]# echo $?
0
[]是不能用这样的符号的例如 < >。
[root@localhost ~]# [ 1 > 2 ]
[root@localhost ~]# echo $?
0
[root@localhost ~]# [ 2 > 1 ]
[root@localhost ~]# echo $?
0
((<))在两个括号中间可以不用对内容进行空格,并且可以执行<和>号
[root@localhost ~]# ((2>1))
[root@localhost ~]# echo $?
0
[root@localhost ~]#
=~ (等效于包含什么什么)
[root@localhost ~]# [[ "hello" =~ "p" ]]
[root@localhost ~]# echo $?
1
[root@localhost ~]# [[ "hello" =~ "h" ]]
[root@localhost ~]# echo $?
0
区别3:
在[ ]中使用 -a 表示逻辑与(两个都得满足) 和 -o 表示逻辑或 (满足其中一个就行)。
[[ ]]使用 && 逻辑与((前一个执行成功 后一个才执行),|| 表示 逻辑或(前一个执行失败 后一个才执行)。
[[ ]]不支持-a
区别4:
在[ ]中==是字符匹配(精确的)
[[ ]]中==是模式匹配(模糊的,可能是某一些)
区别5:
[ ]不支持正则匹配,
[[ ]]支持用=~(等效于包含什么什么)进行正则匹配
区别6:
[ ]仅在部分Shell中支持用()
进行分组,[[ ]]均支持
区别7:
在[ ]中如果变量没有定义,那么需要用双引号引起来(表示字符串)
在[[ ]]中不需要
字符串(sting),简写的时候写str
定义:1.啥也不写,2.单引号,3.双引号。
[root@localhost ~]# str1=hello
[root@localhost ~]# echo $str1
hello
[root@localhost ~]# str2='hello'
[root@localhost ~]# echo $str2
hello
[root@localhost ~]# str3="hello"
[root@localhost ~]# echo $str3
hello
[root@localhost ~]#
‘ ‘ 引用不了变量值
“”或者啥也不因引用,会调出来原有字符串的含义
#当没有引起来的时候,$是被认识的
[root@localhost ~]# echo $str1$str2
hellohello
[root@localhost ~]# echo $str1 $str2
hello hello
[root@localhost ~]# echo $str1 $str2
hello hello
#用引号是代表一个整体
[root@localhost ~]# echo "$str1 $str2"
hello hello
#用!号在里面没有任何含义
[root@localhost ~]# echo "$str1 $str2!"
hello hello!
#但是在外面!是有含义的,代表调取上一个ls的命令。
[root@localhost ~]# !ls
获取字符串的长度
[root@localhost ~]# str1=hello
[root@localhost ~]# echo ${#str1}
5
[root@localhost ~]#
截取字符信息(:0是从左到右进行,:0-1则为从右到左进行)
[root@localhost ~]# str1=hello
#截取hllo,(:0代表的从左到右第一个字符, :1代表的是从左到右输出1个字符)
[root@localhost ~]# echo ${str1:0:1}
h
#截取hllo,(:0代表的从左到右第一个字符, :1代表的是从左到右输出2个字符)
[root@localhost ~]# echo ${str1:0:2}
he
#截取hllo,(:1代表的从左到右第2个字符, :1代表的是从左到右输出1个字符)
[root@localhost ~]# echo ${str1:1:1}
e
#截取hllo,(:1代表的从左到右第2个字符, :1代表的是从左到右输出2个字符)
[root@localhost ~]# echo ${str1:1:2}
el
#截取hllo,(:1代表的从右到左第1个字符, :1代表的是从左到右输出2个字符)
[root@localhost ~]# echo ${str1:0-1:2}
o
#同理
[root@localhost ~]# echo ${str1:0-1:1}
o
[root@localhost ~]# echo ${str1:0-2:1}
l
[root@localhost ~]# echo ${str1:0-2:2}
lo
对字符串内容进行正输出和反输出
#横向反输出hello
[root@localhost ~]# str1=hello
[root@localhost ~]# echo ${str1:0-1:1}${str1:0-2:1}${str1:0-3:1}${str1:0-4:1}${str1:0-5:1}
olleh
[root@localhost ~]#
#竖向反输出(ken代表的是字符串的长度)
[root@localhost ~]# str1=hello
[root@localhost ~]# len=${#str1}
[root@localhost ~]# echo $len
5
[root@localhost ~]# for i in `seq 1 $len`;do echo ${str1:0-$i:1};done
o
l
l
e
h
[root@localhost ~]#
#利用for语句进行反输出(vim编辑中的最后一个echo是为了让输出内容进行换行)
[root@localhost ~]# vim m123.sh
#!/bin/bash
str1=$1
len=${#str1}
for i in `seq 1 $len`
do
echo -n ${str1:0-$i:1}
done
echo
[root@localhost ~]# chmod +x m123.sh
[root@localhost ~]# ./m123.sh 123
321
从指定字符串开始截取(意思就是用#号截右边的所有内容,要么就是用%号截取左边的所有内容)
# 对内容从左向右第1个l开始截,保留右边的内容。(用#号)
[root@localhost ~]# str1=hello
[root@localhost ~]# echo ${str1#*l}
lo
# 对内容从左向右第2个l开始截,保留右边的内容。(用#号)
[root@localhost ~]# echo ${str1##*l}
o
# 对内容从右向左第1个l开始截,保留左边的内容。(用%号)
[root@localhost ~]# echo ${str1%l*}
hel
# 对内容从右向左第2个l开始截,保留左边的内容。(用%号)
[root@localhost ~]# echo ${str1%%l*}
he
#也可以多个字符截取
[root@localhost ~]# echo ${str1#*el}
lo
#实战效果
[root@localhost ~]# str1=/dev/sdb
[root@localhost ~]# echo ${str1##*/}
sdb
[root@localhost ~]# str1=/etc/yum.repos.d
[root@localhost ~]# echo ${str1##*/}
yum.repos.d
[root@localhost ~]#
printf格式化输出(利用该命令进行设定内容)
常用格式替换符
替换符 | 功能 |
---|---|
%s | 字符串 |
%f | 浮点格式,保留小数点位数%.nf,n为数字 |
%b | 相对应的参数中包括转义字符时,可以使用此替换符进行替换,对应的转义字符会被转义 |
%c | ASCII字符,即显示对应参数的第一个字符 |
%d,%i | 十进制整数 |
%o | 八进制值 |
%u | 不带正负号的十进制值 |
%x | 十六进制值(a-f) |
%X | 十六进制值(A-F) |
%% | 表示%本身 |
常用转义字符
\n | 换行 |
---|---|
\r | 回车 |
\t | 水平制表符 |
#可以进行换行
[root@localhost ~]# printf "a\n"
a
#百分数就是(%d%)但为10进制数,s%就是字符串(自行设定内容)
[root@localhost ~]# printf "我名字是%s.今年%d岁! \n " lynn 23
我名字是lynn.今年23岁!
[root@localhost ~]# printf "我名字是%s.今年%d岁! \n " lynn 23.5
-bash: printf: 23.5: 无效的数字
我名字是lynn.今年23岁!
[root@localhost ~]#
#保留几位小数就是(%.几f),s%就是字符串(自行设定内容)
[root@localhost ~]# printf "我名字是%s.今年%.2f岁! \n " lynn 23.5
我名字是lynn.今年23.50岁!
[root@localhost ~]# printf "我名字是%s.今年%.2f岁! \n " lynn 23.54
我名字是lynn.今年23.54岁!
[root@localhost ~]# printf "我名字是%s.今年%.3f岁! \n " lynn 23.546
我名字是lynn.今年23.546岁!
[root@localhost ~]#
#对中间内容进行空格,(正的就是内容左边空几个,负的就是内容右边空几个)
[root@localhost ~]# printf "我名字是%10s.今年%d岁! \n " lynn 23
我名字是 lynn.今年23岁!
[root@localhost ~]# printf "我名字是%-10s.今年%d岁! \n " lynn 23
我名字是lynn .今年23岁!
[root@localhost ~]#
变量测试
1.变量测试
-v(看变量是否被定义)
#检测出str5没有被定义内容
[root@localhost ~]# [ -v str5 ]
[root@localhost ~]# echo $?
1
-R(看变量是否被引用
#可以看到是str5设置的变量,没有被用过
[root@localhost ~]# str5=666
[root@localhost ~]# [ -R str5 ]
[root@localhost ~]# echo $?
127
2.文件测试表达式
拓展:以前的知识:bash 后面跟文件名,可以直接执行该文件。
下面表格的选项可以按照案例格式,对文件进行检测
常用的文件测试操作符 | 说明 |
---|---|
-a/-e 文件 | 文件是否存在 |
-d 文件 | 文件存在且为目录则为真,即测试表达式成立 |
-f 文件 | 文件存在且为普通文件则为真,即测试表达式成立 |
-r 文件 | 文件存在且可读为真 |
-w 文件 | 文件存在且可写为真 |
-x 文件 | 文件存在且可执行则为真 |
#检测/boot,/boot1,/boot2是否存在 (利用-e)
[root@localhost ~]# [ -e /boot ]
[root@localhost ~]# echo $?
0
[root@localhost ~]# [ -e /boot1 ]
[root@localhost ~]# echo $?
1
[root@localhost ~]# [ -e /boot2 ]
[root@localhost ~]# echo $?
1
[root@localhost ~]#
3.字符串测试
拓展:unset str1(删除字符串1及其信息)
常用字符串测试操作符 | 说明 |
---|---|
-n ”字符串“ | 若字符串的长度不为0,则为真,即测试表达式成立,n可以理解为nozero |
-z ”字符串“ | 若字符串的长度为0,则为真,z可以理解为zero |
> | Ascii码是否大于Ascii码 |
“字符串1” == ”字符串2“ | 若字符串1内容等于字符串2内容,则为真 |
“字符串1” != ”字符串2“ | 若字符串1内容不等于字符串2内容,则为真 |
“字符串1” =~ “字符串2” | 左侧字符串是否能被右侧的PATTERN所匹配。注意:此表达式用于[[ ]]中:扩展的正则表达式 |
#测试字符串123,根据上面的选项,输出为0为符合相应选项要求,非0则不符合。
[root@localhost ~]# str1=123
[root@localhost ~]# [ -n $str1 ]
[root@localhost ~]# echo $?
0
[root@localhost ~]# [ -z $str1 ]
[root@localhost ~]# echo $?
1
[root@localhost ~]#
#利用=~时的操作,注意和上面的案例还是有区别的,中括号多了
[root@localhost ~]# str1=123
[root@localhost ~]# [[ $str1 =~ $str2 ]]
[root@localhost ~]# echo $?
1
[root@localhost ~]# str2=123
[root@localhost ~]# [[ $str1 =~ $str2 ]]
[root@localhost ~]# echo $?
0
拓展:
#利用脚本进行设置信息(通过脚本进行简单的设置)
[root@localhost ~]# vim test.sh
#!/bin/bash
[ -z $1 ] && echo "请输入参数!" || echo ok
[root@localhost ~]# chmod +x test.sh
[root@localhost ~]# ./test.sh
请输入参数!
[root@localhost ~]# ./test.sh 1
ok
4.整数测试
在[ ] 或 test中使用的比较符号 | 在(()) 或 [[ ]]中使用的比较符号(不用这个做数字比较) | 说明 |
---|---|---|
-eq | \== 或 = | 相等,equal |
-ne | != | 不相等,not equal |
-gt | > | 大于,greater than |
-ge | > = | 大于等于,greater equal |
-lt | < | 小于,less than |
-le | < = | 小于等于,less equal |
#利用上面的选项,进行利用功能,下面举例来证明上面选项怎么用
[root@localhost ~]# [ 1 -eq 1 ]
[root@localhost ~]# echo $?
0
[root@localhost ~]# [ 1 -ne 1 ]
[root@localhost ~]# echo $?
1
[root@localhost ~]#
逻辑操作符
在[ ] 中使用的操作符 | 在test, [[ ]] , (( ))中使用的逻辑操作符 | 说明 |
---|---|---|
-a | && | and,与,两边都为真,则结果为真 |
-o | || | or,或,有真则真,同假则假 |
! | ! | not,非,两端相反,则结果相反 |
#举例说明,根据上面选项,符合要求输出为0,不符合就为非0。举例看格式
[root@localhost ~]# [ 1 -eq 3 -o 1 -gt 0 ]
[root@localhost ~]# echo $?
0
[root@localhost ~]# [ 1 -eq 3 -a 1 -gt 0 ]
[root@localhost ~]# echo $?
1
[root@localhost ~]#
拓展:
#利用&&和|| 操作,需添加中括号
[root@localhost ~]# [[ 1 -eq 3 && 1 -gt 0 ]]
[root@localhost ~]# echo $?
1
[root@localhost ~]# [[ 1 -eq 3 || 1 -gt 0 ]]
[root@localhost ~]# echo $?
0
[root@localhost ~]#
关于()与 { }
案例
#括号内的内容为子内容,子内容不用影响夫内容。
[root@localhost ~]# name=lisi;(echo $name;name=zhangsan;echo $name);echo $name
lisi
zhangsan
lisi
[root@localhost ~]#
#{}这个括号,会影响到父内容。
[root@localhost ~]# name=lisi; { echo $name;name=zhangsan;echo $name; } ;echo $name
lisi
zhangsan
zhangsan
[root@localhost ~]#
实验演示1
#磁盘空间的判断(看磁盘是不是充足),若超出80则提示空间不足。(没使用脚本)
[root@localhost ~]# df -Th
文件系统 类型 大小 已用 可用 已用% 挂载点
/dev/mapper/openeuler-root ext4 69G 2.8G 63G 5% /
devtmpfs devtmpfs 4.0M 0 4.0M 0% /dev
tmpfs tmpfs 1.7G 0 1.7G 0% /dev/shm
tmpfs tmpfs 4.0M 0 4.0M 0% /sys/fs/cgroup
efivarfs efivarfs 256K 51K 201K 21% /sys/firmware/efi/efivars
tmpfs tmpfs 675M 13M 662M 2% /run
tmpfs tmpfs 1.7G 0 1.7G 0% /tmp
/dev/sda2 ext4 974M 197M 710M 22% /boot
/dev/sda1 vfat 599M 6.1M 593M 2% /boot/efi
/dev/mapper/openeuler-home ext4 124G 40K 118G 1% /home
[root@localhost ~]# jkl_=$(df |head -2 |tail -1 |tr -s ' '|cut -d ' ' -f5 |cut -d"%" -f1)
[root@localhost ~]# echo $jkl_
5
[root@localhost ~]# [ $jkl_ -gt 80 ] && echo "磁盘空间不足" || echo " 磁盘空间充足"
磁盘空间充足
实验演示2
使用read命令命令来接受输入
Option | 说明 |
---|---|
-a array | 把读取的数据赋值给数组array,从下标0开始 |
-p prompt | 显示提示信息,提示内容为prompt |
-s | 静默模式(Silent mode),不会再屏幕上显示输入的字符。例如:输入密码 |
-t seconds | 设置超时时间,单位为秒。如果用户没能按时完成,返回一个非0的退出状态 |
#使用read,赋值多个数据进行输出,-p的选项为显示提示信息。注意看变量信息。-s为静默密码(在输入密码过程时没有显示)。
[root@localhost ~]# vim test.sh
#!/bin/bash
read -p "请输入你的姓名: " name
echo "Hello,$name !"
read -p "请输入你的年龄: " age
[ $age -lt 30 ] && echo "Oh,行!" || echo "Oh,deadman,deadman!"
read -s -p "请输入密码" password
echo $password
[root@localhost ~]# chmod +x test.sh
[root@localhost ~]# ./test.sh
请输入你的姓名: lynn
Hello,lynn !
请输入你的年龄: 26
Oh,行!
请输入密码123
[root@localhost ~]# ./test.sh
请输入你的姓名: lynn
Hello,lynn !
请输入你的年龄: 66
Oh,deadman,deadman!
请输入密码123
[root@localhost ~]#
流程控制
就是控制下当前的语句是否进行执行,或者是控制当前的语句在特定的条件下再执行。(可以让多个不同的指令相互关联起来了。)
if条件语句
1.单分支if
[root@localhost ~]# vim if.sh
#!/bin/bash
#单分支
read -p "请输入一个数字: " number1
if [ $number1 -gt 10 ]
then
echo "$number1 大于 10"
fi
[root@localhost ~]# chmod +x if.sh
[root@localhost ~]# ./if.sh
请输入一个数字: 66
66 大于 10
[root@localhost ~]#
2.双分支if
[root@localhost ~]# vim if.sh
#!/bin/bash
#双分支
read -p "请输入一个数字: " number1
if [ $number1 -gt 10 ]
then
echo "$number1 大于 10"
else
echo "$number1 不大于 10"
fi
[root@localhost ~]# ./if.sh
请输入一个数字: 8
8 不大于 10
[root@localhost ~]#
3.多分支if(多条件)
[root@localhost ~]# vim if.sh
#!/bin/bash
#双分支
read -p "请你的成绩: " socore
if [ $socore -ge 90 -a $socore -le 100 ]
then
echo "A"
elif [ $socore -ge 80 -a $socore -le 89 ]
then
echo "B"
elif [ $socore -ge 60 -a $socore -le 79 ]
then
echo "C"
elif [ $socore -ge 0 -a $socore -le 59 ]
then
echo "D"
else
echo "请输入0~100的成绩"
fi
[root@localhost ~]# ./if.sh
请你的成绩: 59
D
[root@localhost ~]# ./if.sh
请你的成绩: 101
请输入0~100的成绩
[root@localhost ~]# ./if.sh
请你的成绩: 66
C
[root@localhost ~]# ./if.sh
请你的成绩: 100
A
[root@localhost ~]#
4.父子if语句
#父子if条件命令的演示(利用脚本,以sshd服务为例,如果关闭就尝试将其打开)
[root@localhost ~]# vim sshd_check_staus.sh
#!/bin/bash
process_stat=$(netstat -anpt | grep sshd | wc -l)
if [ $process_stat -gt 0 ];then
echo "sshd is running"
systemctl status sshd | grep "active"
else
echo "sshd is running"
systemctl start sshd
sleep 2
process_stat=$( netstat -anpt | grep sshd wc -l )
if [ $process_stat -gt 0 ];then
echo "sshd run successful"
else
echo "sshd no method to start,please check"
fi
fi
[root@localhost ~]# chmod +x sshd_check_staus.sh
[root@localhost ~]# ./sshd_check_staus.sh
sshd is running
Active: active (running) since Tue 2025-09-02 16:52:26 CST; 8h ago
└─163391 grep active