Shell中的数值运算
Posted Serenity(朱庆柏)
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Shell中的数值运算相关的知识,希望对你有一定的参考价值。
Shell中的数值运算
案例1:Shell中的数值运算
案例2:条件测试操作
案例3:使用if选择结构
1案例1:Shell中的数值运算
1.1问题
本案例要求熟悉Linux Shell环境的特点,主要练习以下操作:
使用expr、$[]、let等整数运算工具:定义变量X=1234,然后计算X与78的四则运算及求模结果
使用bc实现小数运算操作:以交互方式计算12.34与56.78的四则运算结果,另外再以非交互方式重复上述计算,最多显示4位小数
1.2步骤
实现此案例需要按照如下步骤进行。
步骤一:整数运算工具
1)使用expr命令
乘法操作应采用*转义,避免被作为Shell通配符;参与运算的整数值与运算操作符之间需要以空格分开,引用变量时必须加$符号。
首先定义变量X=1234,然后分别计算与78的加减乘除和求模运算结果:
[root@svr5~]#X=1234//定义变量X
[root@svr5~]#expr $X+78//加法
1312
[root@svr5~]#expr $X-78//减法
1156
[root@svr5~]#expr $X*78//乘法,操作符应添加转义
96252
[root@svr5~]#expr $X/78//除法,仅保留整除结果
15
[root@svr5~]#expr $X%78//求模
64
2)使用$[]或$(())表达式
乘法操作*无需转义,运算符两侧可以无空格;引用变量可省略$符号;计算结果替换表达式本身,可结合echo命令输出。
同样对于变量X=1234,分别计算与78的加减乘除和求模运算结果:
[root@svr5~]#X=1234
[root@svr5~]#echo $[X+78]
1312
[root@svr5~]#echo $[X-78]
1156
[root@svr5~]#echo $[X*78]
96252
[root@svr5~]#echo $[X/78]
15
[root@svr5~]#echo $[X%78]
64
3)使用let命令
expr或$[]、$(())方式只进行运算,并不会改变变量的值;而let命令可以直接对变量值做运算再保存新的值。因此变量X=1234,在执行let运算后的值会变更;另外,let运算操作并不显示结果,但是可以结合echo命令来查看:
[root@svr5~]#X=1234
[root@svr5~]#let y=X+22
[root@svr5~]#echo $y
1256
[root@svr5~]#let X++;echo $X #X++(X=X+1)
[root@svr5~]#let X--;echo $X #X--(X=X-1)
[root@svr5~]#let X+=78;echo $X #X+=78(X=X+78)
[root@svr5~]#let X-=78;echo $X #X-=78(X=X-78)
[root@svr5~]#let X*=78;echo $X #X*=78(X=X*78)
[root@svr5~]#let X/=78;echo $X #X/=78(X=X/78)
[root@svr5~]#let X%=78;echo $X #X%=78(X=X%79)
步骤二:小数运算工具
1)bc交互式运算
先执行bc命令进入交互环境,然后再输入需要计算的表达式。以计算小数12.34与5.678的四则运算为例,相关操作如下:
[root@svr5~]#bc
bc 1.06.95
Copyright 1991-1994,1997,1998,2000,2004,2006 Free Software
Foundation,Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type`warranty‘.
12.34+56.78//加法
69.12
12.34-56.78//减法
-44.44
12.34*56.78//乘法
700.66
12.34/56.78//除法
0
quit//退出交互计算器
[root@svr5~]#
2)bc非交互式运算
将需要运算的表达式通过管道操作交给bc运算。注意,小数位的长度可采用scale=N限制,除此以外也受参与运算的数值的小数位影响。以计算小数12.34与5.678的四则运算为例,相关操作如下:
[root@svr5~]#echo ‘scale=4;12.34+5.678‘ | bc
18.018
[root@svr5~]#echo ‘scale=4;12.34-5.678‘ | bc
6.662
[root@svr5~]#echo ‘scale=4;12.34 * 5.678‘ | bc
70.0665
[root@svr5~]#echo ‘scale=4;12.34/5.678‘ | bc
2.1733
案例2:条件测试操作
2.1问题
本案例要求参考PPT上的示例,分别练习以下条件测试操作:
字符串匹配
比较整数值的大小
识别文件/目录的状态
多个条件/操作的逻辑组合
2.2步骤
实现此案例需要按照如下步骤进行。
步骤一:条件测试的基本用法
1)语法格式
使用“test表达式”或者[表达式]都可以,表达式两边至少要留一个空格。
条件测试操作本身不显示出任何信息。测试的条件是否成立主要体现在命令执行后的返回状态(即$?),所以可以在测试后查看变量$?的值来做出判断,或者结合&&、||等逻辑操作显示出结果(或作其他操作)。
步骤二:字符串测试
1)==比较两个字符串是否相同
检查当前用户是否为root。
当root用户执行时:
[root@svr5~]#[ $USER=="root" ]//测试
[root@svr5~]#echo $?//查看结果0为对,非0为错
当普通用户执行时:
[zengye@svr5~]$[ $USER=="root" ]
[zengye@svr5~]$echo $?
2)!=比较两个字符串是否不相同
检查当前用户,如果不是root。
当普通用户执行时:
[zengye@svr5~]$[ $USER!="root" ]
当root用户执行时:
[root@svr5~]#[ $USER!="root" ]
3)一行执行多条命令的情况
#A&&B//仅当A命令执行成功,才执行B命令
#A||B//仅当A命令执行失败,才执行B命令
#A;B//执行A命令后执行B命令,两者没有逻辑关系
4)-z检查变量的值是否未设置(空值)
[root@svr5~]#var1="nb";var2=""
[root@svr5~]#[ -z "$var1" ] && echo "空值" || echo "非空值"
非空值
[root@svr5~]#[ -z $var2 ] && echo "空值" || echo "非空值"
空值//变量var2已设置,但无任何值,视为空
[root@svr5~]#[ !-z $var1 ]//测试var1是否为非空
步骤三:整数值比较
参与比较的必须是整数(可以调用变量),比较非整数值时会出错:
[root@svr5~]#A=20.4
[root@svr5~]#[ $A -gt 10 ] //不支持小数比较
-bash:[:20.4:integer expression expected
1)-eq比较两个数是否相等。
[root@svr5~]#X=20//定义一个测试变量
[root@svr5~]#[ $X -eq 20 ] && echo "相等" || echo "不相等"
相等
[root@svr5~]#[ $X -eq 30 ] && echo "相等" || echo "不相等"
不相等
2)-ne比较两个数是否不相等。
[root@svr5~]#X=20//定义一个测试变量
[root@svr5~]#[ $X -ne 20 ] && echo "不等于" || echo "等于"
等于
[root@svr5~]#[ $X -ne 30 ] && echo "不等于" || echo "等于"
不等于
3)-gt比较前面的整数是否大于后面的整数。
[root@svr5~]#X=20//定义一个测试变量
[root@svr5~]#[ $X -gt 10 ] && echo "大于" || echo "否"
大于
[root@svr5~]#[ $X -gt 20 ] && echo "大于" || echo "否"
否
[root@svr5~]#[ $X -gt 30 ] && echo "大于" || echo "否"
否
4)-ge比较前面的整数是否大于或等于后面的整数。
[root@svr5~]#X=20//定义一个测试变量
[root@svr5~]#[ $X -ge 10 ] && echo "大于或等于" || echo "否"
大于或等于
[root@svr5~]#[ $X -ge 20 ] && echo "大于或等于" || echo "否"
大于或等于
[root@svr5~]#[ $X -ge 30 ] && echo "大于或等于" || echo"否"
否
5)-lt比较前面的整数是否小于后面的整数。
[root@svr5~]#X=20//定义一个测试变量
[root@svr5~]#[ $X -lt 10 ] && echo "小于" || echo "否"
否
[root@svr5~]#[ $X -lt 20 ] && echo "小于" || echo "否"
否
[root@svr5~]# [ $X -lt 30 ] && echo "小于" || echo"否"
小于
6)-le比较前面的整数是否小于或等于后面的整数。
[root@svr5~]#X=20//定义一个测试变量
[root@svr5~]#[ $X -le 10 ] && echo "小于或等于" || echo "否"
否
[root@svr5~]#[ $X -le 20 ] && echo "小于或等于" || echo "否"
小于或等于
[root@svr5~]#[ $X -le 30 ] && echo "小于或等于" || echo"否"
小于或等于
7)提取当前登录的用户数,比较是否超过5。
[root@svr5~]#who | wc -l//确认已登录的用户数
4
[root@svr5~]#N=$( who | wc -l )//赋值给变量N
[root@svr5~]#[ $N -gt 5 ] && echo "超过了" || echo "没超过"
没超过
上述赋值给变量N及与5比较的操作,可以简化为如下形式:
[root@svr5~]#[ $(who | wc -l ) -gt 5 ] && echo "超过了" || echo "没超过"
没超过
步骤四:识别文件/目录的状态
1)-e判断对象是否存在(不管是目录还是文件)
[root@svr5~]#[ -e "/usr/" ] && echo "存在" || echo "不存在"
存在
[root@svr5~]# [ -e "/etc/fstab" ] && echo "存在" || echo "不存在"
存在
[root@svr5~]#[ -e "/home/nooby" ] && echo "存在" || echo "不存在"
不存在
2)-d判断对象是否为目录(存在且是目录)
[root@svr5~]#[ -d "/usr/" ] && echo "是目录" || echo "不是目录"
是目录
[root@svr5~]#[ -d "/etc/fstab" ] && echo "是目录" || echo "不是目录"
不是目录
[root@svr5~]#[ -d "/home/nooby" ] && echo "是目录" || echo "不是目录"
不是目录
3)-f判断对象是否为文件(存在且是文件)
[root@svr5~]#[ -f "/usr/" ] && echo "是文件" | |echo "不是文件"
不是文件
[root@svr5~]#[-f "/etc/fstab" ] && echo "是文件" || echo "不是文件"
是文件
[root@svr5~]#[ -f "/home/nooby" ] && echo "是文件" || echo "不是文件"
不是文件
4)-r判断对象是否可读
此测试对root用户无效,无论文件是否设置r权限,root都可读:
[root@svr5~]#cp /etc/hosts /tmp/test.txt//复制一个文件做测试
[root@svr5~]#chmod -r /tmp/test.txt//去掉所有的r权限
[root@svr5~]#[ -r "/tmp/test.txt" ] && echo "可读" || echo "不可读"
可读//root测试结果仍然可读
切换为普通用户,再执行相同的测试,结果变为“不可读”:
[zengye@svr5~]$[ -r "/tmp/test.txt" ] && echo "可读" || echo "不可读"
不可读
5)-w判断对象是否可写
此测试同样对root用户无效,无论文件是否设置w权限,root都可写:
[root@svr5~]#chmod -w /tmp/test.txt//去掉所有的w权限
[root@svr5~]#ls -l /tmp/test.txt//确认设置结果
----------1 root root 33139 12-11 10:43/tmp/test.txt
[root@svr5~]#[ -w "/tmp/test.txt" ] && echo "可写" || echo "不可写"
可写
切换为普通用户,可以正常使用-w测试:
[zengye@svr5~]$ls -l /tmp/test.txt
----------1 root root 33139 12-11 10:52/tmp/test.txt
[zengye@svr5~]$[ -w "/tmp/test.txt" ] && echo "可写" || echo "不可写"
不可写
6)-x判断对象是否具有可执行权限
这个取决于文件本身、文件系统级的控制,root或普通用户都适用:
[root@svr5~]#chmod 644 /tmp/test.txt//重设权限,无x
[root@svr5~]#ls -l /tmp/test.txt//确认设置结果
-rw-r--r--1 root root 33139 12-11 10:52/tmp/test.txt
[root@svr5~]#[ -x "/tmp/test.txt" ] && echo "可执行" || echo "不可执行"
不可执行
[root@svr5~]#chmod +x /tmp/test.txt//添加x权限
[root@svr5~]#[ -x "/tmp/test.txt" ] && echo "可执行" || echo"不可执行"
可执行
步骤五:多个条件/操作的逻辑组合
1)&&,逻辑与
给定条件必须都成立,整个测试结果才为真。
检查变量X的值是否大于10,且小于30:
[root@svr5~]#X=20//设置X变量的值为20
[root@svr5~]#[ $X -gt 10 ] && [ $X -lt 30 ] && echo "YES"
YES
2)||,逻辑或
只要其中一个条件成立,则整个测试结果为真。
只要/tmp/、/var/spool/目录中有一个可写,则条件成立:
[root@svr5~]#[ -w "/tmp/" ] || [ -w "/var/spool/" ] && echo "OK"
OK
案例3:使用if选择结构
3.1问题
本案例要求编写3个Shell脚本,分别实现以下目标:
检测/media/cdrom目录,若不存在则创建
检测并判断指定的主机是否可ping通
从键盘读取一个论坛积分,判断论坛用户等级,等级分类如下:
大于等于90神功绝世
大于等于80,小于90登峰造极
大于等于70,小于80炉火纯青
大于等于60,小于70略有小成
小于60初学乍练
3.2方案
if单分支的语法组成:
if条件测试
then命令序列
fi
if双分支的语法组成:
if条件测试
then命令序列1
else命令序列2
fi
if多分支的语法组成:
if条件测试1
then命令序列1
elif条件测试2
then命令序列2
else命令序列n
fi
if多分支结构实际上相当于多层if嵌套:
if条件测试1;then
命令序列1
else
if条件测试2;then
命令序列2
else
....
命令序列n
fi
fi
3.3步骤
实现此案例需要按照如下步骤进行。
步骤一:检测/media/cdrom目录,若不存在则创建
1)编写脚本如下:
[root@svr5~]#vim mountdir.sh
#!/bin/bash
dir="/media/cdrom/"
if[ ! -d $dir ]
then
mkdir -p $dir
fi
[root@svr5~]#chmod +x mountdir.sh//添加可执行权限
2)测试、验证脚本功能
[root@svr5~]#ls -ld /media/cdrom//本来没有/media/cdrom目录
ls:/media/cdrom:没有那个文件或目录
[root@svr5~]#./mountdir.sh//执行脚本
[root@svr5~]#ls -ld /media /cdrom//再检查已经有了
drwxr-xr-x 2 root root 4096 12-11 15:16/media/cdrom
有了/media/cdrom文件夹以后,再次执行上述脚本,实际上不做任何有效操作:
[root@svr5~]#./mountdir.sh
[root@svr5~]#
步骤二:检测并判断指定的主机是否可ping通
1)分析任务需求
使用ping命令检测目标主机时,人工可直接判断反馈结果,而脚本却不方便。但是当ping测试成功时,执行状态$?的值为0;而ping测试失败时,$?的值不为0。因此在Shell脚本中可以利用这一点来判断ping目标主机的成败。
为了节省ping测试时间,可以只发送3个测试包(-c 3)、缩短发送测试包的间隔秒数(-i 0.2)、等待反馈的超时秒数(-W 3)。比如,检查可ping通的主机:
[root@svr5~]#ping-c 3-i 0.2-W 3 192.168.4.5
PING 192.168.4.5(192.168.4.5)56(84)bytes of data.
64 bytes from 192.168.4.5:icmp_seq=1 ttl=64 time=0.131 ms
64 bytes from 192.168.4.5:icmp_seq=2 ttl=64 time=0.076 ms
64 bytes from 192.168.4.5:icmp_seq=3 ttl=64 time=0.073 ms
---192.168.4.5 ping statistics---
3 packets transmitted,3 received,0%packet loss,time 402ms
rtt min/avg/max/mdev=0.073/0.093/0.131/0.027 ms
[root@svr5~]#echo$?//执行状态表示成功
0
2)脚本编写参考如下:
[root@svr5~]#vim pinghost.sh
#!/bin/bash
ping -c 3 -i 0.2 -W 3 $1 &> /dev/null
if [ $? -eq 0];then
echo "Host $1 is up."
else
echo "Host $1 is down."
fi
[root@svr5~]#chmod +x pinghost.sh
3)测试、验证脚本功能
[root@svr5~]#./pinghost.sh 192.168.4.5
Host 192.168.4.5 is up.
[root@svr5~]#./pinghost.sh 192.168.4.50
Host 192.168.4.50 is down.
步骤三:从键盘读取一个论坛积分,判断论坛用户等级
1)脚本编写参考如下:
大于等于90神功绝世
大于等于80,小于90登峰造极
大于等于70,小于80炉火纯青
大于等于60,小于70略有小成
大于60初学乍练
[root@svr5~]#vim gradediv.sh
#!/bin/bash
read -p "请输入积分(0-100):" JF
if [ $JF -ge 90 ];then
echo"$JF分,神功绝世"
elif [ $JF -ge 80 ];then
echo "$JF分,登峰造极"
elif [ $JF -ge 70 ];then
echo "$JF分,炉火纯青"
elif[ $JF–ge 60 ];then
echo "$JF分,略有小成"
else
echo "$JF分,初学乍练"
fi
[root@svr5~]#chmod +x gradediv.sh
3)测试、验证脚本
[root@svr5~]#./gradediv.sh
请输入积分(0-100):74
74分,炉火纯青
[root@svr5~]#./gradediv.sh
请输入分数(0-100):68
68分,略有小成
[root@svr5~]#./gradediv.sh
请输入分数(0-100):87
87分,登峰造极
以上是关于Shell中的数值运算的主要内容,如果未能解决你的问题,请参考以下文章
Shell编程Shell中Bash变量-数值运算运算符变量测试和内容替换