5、表达式

运算符

运算符基础

基础知识

需求

	根据我们之前的学习,通过现有的知识内容可以完成一个简单的功能操作,即使通过所谓的脚本参数可以实现一个脚本在多个数据值的情况下实现不同的结果。但是问题是,目前脚本本身还没有实现灵活的机制。
	所谓脚本级别的灵活机制,说的是脚本内部能够实现数据的操作和判断。而数据的操作也是判断过程中必须的一个条件组成部分。所以数据的操作是脚本的一个核心内容。

数据操作

	关于shell可以实施的数据操作,按照不同的业务场景主要可以分为如下两个方面:
	运算符 - 数据值之间的操作
		赋值运算
			- 结果值输出
			- 示例: =、*=、/=、%=、+=、-=、<<=、>>=、&=、^=、|=等
		二元运算 
			- 数据值操作
			- 示例: +、-、*、/、%等
		高阶运算
			- 更高一级的数学运算
			- 示例:**、^、++、--、
		其他运算 
			- 其他运算操作
			- 示例:<<、>>等
注意:
	这些所谓的运算符一般很难单独来使用,都需要结合计算表达式来实现相应的效果
	表达式 - 数据值在特定场景的运算符操作
		计算表达式
			- 将多个值的运算操作执行起来
			- 示例:bc、let、expr、$(())等
		测试表达式 
			- 判断结果值是否满足需求
			- 示例:test、[]等
		逻辑表达式 
			- 多条件的多场景组合
			- 示例:&&、||、and、or、not、&、|等
		比较表达式 
			- 判断数据之间的适配关系
			- 示例:-f|d|s、-r|x|w、-e、-n、==、!=、>、<、<=、>=等
		三元表达式
			- 多逻辑的简单计算表达式
			- 示例:expr?expr:expr
		集合表达式 
			- 表达式之间的关联关系
			- 示例:expr1 , expr2、 [[ ]]、[ -a|o ]、[ ! ]等

赋值运算

基础知识

	所谓的赋值运算,其实指的就是将一个值赋予一个变量名,虽然我们在变量一节中对于该知识点进行了一些基础性的操作,但是赋值运算仍然还有一些其他的表现样式,尤其是关于命令计算相关的。

简单的赋值操作

为变量赋值
[root@localhost ~]# echo $num $string

[root@localhost ~]# num=123 string=abc
[root@localhost ~]# echo $num $string
123 abc
赋值时候定制属性
[root@localhost ~]# declare stringnum=123
[root@localhost ~]# declare string=nihao
[root@localhost ~]# echo $stringnum $string
123 nihao
[root@localhost ~]# declare -i num=654
[root@localhost ~]# declare -i num2=aaa
[root@localhost ~]# echo $num $num2
654 0
获取特定数据
[root@localhost ~]# myuser=$(whoami)
[root@localhost ~]# echo $myuser
root
[root@localhost ~]# kernel_info=$(cat /etc/redhat-release)
[root@localhost ~]# echo $kernel_info
CentOS Linux release 7.9.2009 (Core)

image

简单计算

$[]

简介

	$[]方法,常用于整数计算场景,适合不太复杂的计算,运算结果是小数的也会自动取整。
	后面的几种也是一样

格式

方法1:
	$[计算表达式]
方法2:
	a=$[变量名a+1]

注意:
	这里的表达式可以不是一个整体

简单示例

简单运算
[root@localhost ~]# echo $[100/5]
20
[root@localhost ~]# echo $[ 2 + 5 ]
7

变量参与运算
[root@localhost ~]# a=6
[root@localhost ~]# a=$[a+1]
[root@localhost ~]# echo $a
7

运算结果取整
[root@localhost ~]# echo $[100/3]
33

image

let

简介

	let是另外一种相对来说比较简单的数学运算符号了

格式

let	变量名a=变量名a+1

注意:
	表达式必须是一个整体,中间不能出现空格等特殊字符

简单示例

简单运算
[root@localhost ~]# i=1
[root@localhost ~]# let i=i+7
[root@localhost ~]# echo $i
8

let表达式必须是一个整体
[root@localhost ~]# let i = i * 2
bash: let: =: 语法错误: 期待操作数 (错误符号是 "=")
[root@localhost ~]# let i=i * 2
bash: let: anaconda-ks.cfg: 语法错误: 无效的算术运算符 (错误符号是 ".cfg")
[root@localhost ~]# let i=i*2
[root@localhost ~]# echo $i
16

image

(())

简介

(())的操作与let基本一致,相当于let替换成了 (())

格式

((变量计算表达式))
注意:
	对于 $(())中间的表达式,可以不是一个整体,不受空格的限制

简单实践

[root@localhost ~]# num1=34
[root@localhost ~]# ((num2=num1+34))
[root@localhost ~]# echo $num2
68

image

$(())

简介

$(())的操作,相当于 (()) + echo $变量名 的组合

格式

echo $((变量计算表达式))
注意:
	对于 $(())中间的表达式,可以不是一个整体,不受空格的限制

简单实践

[root@localhost ~]# num1=34
[root@localhost ~]# echo $((num2=num1+34))
68

image

赋值运算

二元运算

简介

	所谓的二元运算,指的是 多个数字进行+-*/%等运算

简单实践

加法运算
[root@localhost ~]# echo $(( 4 + 4 ))
8
[root@localhost ~]# num1=3 num2=4
[root@localhost ~]# echo $(($num1 + $num2))
7

减法运算
[root@localhost ~]# echo $((8-2))
6
[root@localhost ~]# echo $(($num2-$num1))
1

乘法运算
[root@localhost ~]# echo $((8*2))
16
[root@localhost ~]# echo $(($num2*$num1))
12

除法运算
[root@localhost ~]# echo $((8/2))
4
[root@localhost ~]# echo $(($num2/$num1))
1

取余运算
[root@localhost ~]# echo $((8%3))
2
[root@localhost ~]# echo $(($num2%$num1))
1

image

案例实践-小学计算题

案例需求:鸡兔同笼,共有30个头,88只脚。求笼中鸡兔各有多少只?
查看脚本内容
[root@localhost ~]# cat count_head_feet.sh
#!/bin/bash
# 功能:小学计算题目
# 版本:v0.1
# 作者:书记
# 联系:www.superopsmsb.com

# 定制普通变量
head="$1"
feet="$2"

# 计算逻辑
# 每个头至少两只脚,多出的脚都是兔子的
rabbit_num=$(( ($feet - $head - $head) / 2))
chick_num=$[ $head - $rabbit_num ]

# 结果输出
echo -e "\e[31m\t鸡和兔的数量\e[0m"
echo -e "\e[32m============================"
echo "鸡的数量: ${chick_num}"
echo "兔的数量:${rabbit_num}"
echo -e "============================\e[0m"
脚本执行效果
[root@localhost ~]# /bin/bash count_head_feet.sh 30 88
        鸡和兔的数量
============================
鸡的数量: 16
兔的数量:14
============================

赋值运算

简介

	这里的赋值运算,是一种进阶版本的复制操作,常见的样式如下:
		样式1:+=、-=、*=、/=
			- 在自身的基础上进行二元运算,结果值还是自己
		样式2:++、--
			- 在自身的基础上进行递增和递减操作,结果值还是自己

简单实践

+=运算
[root@localhost ~]# num1=6
[root@localhost ~]# let value+=$num1    # 相当于 let value=value+$num1
[root@localhost ~]# echo $value
6

-=运算
[root@localhost ~]# let value-=2    # 相当于 let value=value-2
[root@localhost ~]# echo $value
4

*=运算
[root@localhost ~]# let value*=2    # 相当于 let value=value*2
[root@localhost ~]# echo $value
8

/=运算
[root@localhost ~]# let value/=2    # 相当于 let value=value/2
[root@localhost ~]# echo $value
4

image

i++运算
[root@localhost ~]# value=9
[root@localhost ~]# let value++		# 相当于 let value=value+1
[root@localhost ~]# echo $value
10
[root@localhost ~]# let value++		# 相当于 let value=value+1
[root@localhost ~]# echo $value
11

++i运算
[root@localhost ~]# let ++value		# 相当于 let value=value+1
[root@localhost ~]# echo $value
12
--运算
[root@localhost ~]# value=9
[root@localhost ~]# let value--		# 相当于 let value=value-1
[root@localhost ~]# echo $value
8
[root@localhost ~]# let value--		# 相当于 let value=value-1
[root@localhost ~]# echo $value
7
[root@localhost ~]# let --value		# 相当于 let value=value-1
[root@localhost ~]# echo $value
6

expr计算

基础知识

简介

	expr即可以做常见的整数运算,还可以做数字比较,字符串计算等操作。

格式

数字场景:
	expr 运算表达式
字符串场景:
	match:用户获取匹配到字符串的长度
		expr match 字符串 匹配内容
	substr:截取字符串
		expr substr 字符串 起始位置 截取长度
		注意:起始位置值>=1
	index:查找第一次匹配字符的位置
		expr index 字符串 字符
	length:计算字符串的长度
		expr length 字符串 

简单实践

数学运算

数学运算
[root@localhost ~]# expr 1 + 2 - 3 \* 4 / 5 + \( 6 - 7 \) \* 8
-7
[root@localhost ~]# x=1
[root@localhost ~]# expr $x + 4
5

image

字符串运算

用户获取匹配到字符串的长度
[root@localhost ~]# file=jdslkfajkldsjafklds
[root@localhost ~]# expr match $file "k.*j"
0
[root@localhost ~]# expr match $file ".*j"
13

截取字符串
[root@localhost ~]# expr substr $file 0 4
[root@localhost ~]# expr substr $file 1 4
jdsl

查找第一次匹配字符的位置
[root@localhost ~]# expr index $file b
0
[root@localhost ~]# expr index $file j
1

计算字符串的长度
[root@localhost ~]# expr length $file
19

image
image
image

bc计算

基础知识

简介

	bc是一种任意精度的计算语言,提供了语法结构,比如条件判断、循环等,功能是很强大的,还能进行进制转换。
常见参数
    -i 强制交互模式;
    -l 使用bc的内置库,bc里有一些数学库,对三角计算等非常实用;
    -q 进入bc交互模式时不再输出版本等多余的信息。
特殊变量
    ibase,obase 用于进制转换,ibase是输入的进制,obase是输出的进制,默认是十进制;
    scale 小数保留位数,默认保留0位。

简单实践

实践1-交互示例

在shell命令行直接输入bc即进入bc语言的交互模式
[root@localhost ~]# bc -l -q
4 / 3
1.33333333333333333333
3 + 4
7
5 * 8
40
exit
0
^C
(interrupt) Exiting bc.
[root@localhost ~]#

image

实践2 - 非交互示例

格式:echo "属性设置; 计算表达式" | bc
[root@localhost ~]# echo "scale=2; 9-8*2/5^2" | bc
8.36
[root@localhost ~]# echo "scale=2; sqrt(10)" | bc
3.16
[root@localhost ~]# echo '45.36-22.33' | bc
23.03

image

实践3 - 内存使用率统计

查看脚本效果
[root@localhost ~]# cat memory_info.sh
#!/bin/bash
# 功能:定制内存信息的使用率
# 版本:v0.1
# 作者:书记
# 联系:www.superopsmsb.com

# 定制基础信息
temp_file='/tmp/free.txt'
hostname=$(hostname)

# 获取内存信息
free -m > /tmp/free.txt
# 获得内存总量
memory_totle=$(grep -i "mem" /tmp/free.txt | tr -s " " | cut -d " " -f2)
# 获得内存使用的量
memory_use=$(grep -i "mem" /tmp/free.txt | tr -s " " | cut -d " " -f3)
# 获得内存空闲的量
memory_free=$(grep -i "mem" /tmp/free.txt | tr -s " " | cut -d " " -f4)

# 定制使用比例
# 获取内存使用率
percentage_use=$(echo "scale=2; $memory_use * 100 / $memory_totle" | bc)
# 定制内存空闲率
percentage_free=$(echo "scale=2; $memory_free * 100 / $memory_totle" | bc)

# 内容信息输出
echo -e "\e[31m\t${hostname} 内存使用信息统计\e[0m"
echo -e "\e[32m=========================================="
echo '内存总量:     ' ${memory_totle}
echo '内存使用量:   ' ${memory_use}
echo '内存空闲量:   ' ${memory_free}
echo '内存使用比率: ' ${percentage_use}
echo '内存空闲利率: ' ${percentage_free}
echo "=========================================="
echo -e "\e[0m"
脚本执行后效果
[root@localhost ~]# /bin/bash memory_info.sh
        localhost 内存使用信息统计
==========================================
内存总量:      3770
内存使用量:    238
内存空闲量:    3376
内存使用比率:  6.31
内存空闲利率:  89.54
==========================================

表达式

基础知识

	所谓的表达式,就是在场景需求的前提下,判断数据和运算符的操作是否满足需求。		

语法格式:

格式
	真实值 操作符 真实值 比较运算符 预期值
示例
	3 + 4 > 6
要点:
	表达式应该具有判断的功能

测试表达式

简介

	Shell环境根据命令执行后的返回状态值($?)来判断是否执行成功,当返回值为0,表示成功,值为其他时,表示失败。使用专门的测试工具---test命令,可以对特定条件进行测试,并根据返回值来判断条件是否成立(返回值0为成立)

测试表达式

样式1: test 条件表达式
样式2: [ 条件表达式 ]
注意:
    以上两种方法的作用完全一样,后者为常用。
    但后者需要注意方括号[、]与条件表达式之间至少有一个空格。
    test跟 [] 的意思一样
        条件成立,状态返回值是0
        条件不成立,状态返回值是1

简单示例

test语法示例
[root@localhost ~]# test 1 == 1
[root@localhost ~]# echo $?
0
[root@localhost ~]# test 1 == 2
[root@localhost ~]# echo $?
1

test -v语法测试变量有没有被设置值,亦可理解为变量是否为空。
[root@localhost ~]# echo $empty

[root@localhost ~]# test -v empty
[root@localhost ~]# echo $?
1
[root@localhost ~]# empty=value
[root@localhost ~]# echo $?
0

image
image

[] 语法示例
[root@localhost ~]# [ 1 == 1 ]
[root@localhost ~]# echo $?
0
[root@localhost ~]# [ 1 == 12 ]
[root@localhost ~]# echo $?
1

image

[] 格式解读
[root@localhost ~]# [ 1 == 12]
bash: [: 缺少 `]'
[root@localhost ~]# [ 1 == 12] ]
[root@localhost ~]# echo $?
1

image

逻辑表达式

基础知识

简介

	逻辑表达式一般用于判断多个条件之间的依赖关系。
	常见的逻辑表达式有:&&  和  ||,根据观察的角度不同含义也不同

语法解读

&&		
	示例:命令1  &&  命令2
		如果命令1执行成功,那么我才执行命令2		-- 夫唱妇随
		如果命令1执行失败,那么命令2也不执行
||	
	示例:命令1 || 命令2
		如果命令1执行成功,那么命令2不执行			-- 对着干
		如果命令1执行失败,那么命令2执行
!
	示例:! 命令
		如果命令执行成功,则整体取反状态

组合使用

使用样式:
	命令1 && 命令2 || 命令3 
    	方便理解的样式  ( 命令1 && 命令2 ) || 命令3
功能解读:
	命令1执行成功的情况下,执行命令2
	命令2执行失败的情况下,执行命令3
注意:
	&& 必须放到前面,|| 放到后面

简单实践

实践1-语法实践

&& 语法实践
[root@localhost ~]# [ 1 = 1 ] && echo "条件成立"
条件成立
[root@localhost ~]# [ 1 = 2 ] && echo "条件成立"

|| 语法实践
[root@localhost ~]# [ 1 = 2 ] || echo "条件不成立"
条件不成立
[root@localhost ~]# [ 1 = 1 ] || echo "条件不成立"
[root@localhost ~]#

image

实践2-案例实践

执行文件前保证具备执行权限
[root@localhost ~]# cat test_argnum.sh
#!/bin/bash
# && 和 || 演示

# 设定普通变量
arg_num=$#

[ $# == 1 ] && echo "脚本参数为1,允许执行脚本"
[ $# == 1 ] || echo "脚本参数不为1,不允许执行脚本"
脚本执行后效果
[root@localhost ~]# /bin/bash test_argnum.sh
脚本参数不为1,不允许执行脚本
[root@localhost ~]# /bin/bash test_argnum.sh 1
脚本参数为1,允许执行脚本
[root@localhost ~]# /bin/bash test_argnum.sh 1 2
脚本参数不为1,不允许执行脚本

image

实践3-取反

查看正常的字符串判断
[root@localhost ~]# [ aaa == aaa ]
[root@localhost ~]# echo $?
0

查看取反的效果判断
[root@localhost ~]# [ ! aaa == aaa ]
[root@localhost ~]# echo $?
1
[root@localhost ~]# [ ! aaa == bbb ]
[root@localhost ~]# echo $?
0

image

实践4 - 组合使用

[root@localhost ~]# [ -d /etc ] && echo "目录存在" || echo "目录不存在"
目录存在
[root@localhost ~]# [ -d /etc1 ] && echo "目录存在" || echo "目录不存在"
目录不存在

实践5 - 主机网络连通性测试

查看脚本内容
[root@localhost ~]# cat host_network_test.sh
#!/bin/bash
# 功能:测试主机网络连通性
# 版本:v0.1
# 作者:书记
# 联系:www.superopsmsb.com

# 定制普通变量
host_addr="$1"

# 脚本基本判断
[ -z ${host_addr} ] && echo "请输入待测试主机ip" && exit
[ $# -ne 1 ] && echo "请保证输入1个脚本参数" && exit

# 测试主机网络
net_status=$(ping -c1 -w1 ${host_addr} >/dev/null 2>&1 && echo "正常" || echo "异常")

# 信息输出
echo -e "\e[31m\t主机网络状态信息\e[0m"
echo -e "\e[32m================================"
echo "${host_addr} 网络状态: ${net_status}"
echo -e "================================\e[0m"
脚本执行效果
[root@localhost ~]# /bin/bash host_network_test.sh
请输入待测试主机ip
[root@localhost ~]# /bin/bash host_network_test.sh aa bb
请保证输入1个脚本参数
[root@localhost ~]# /bin/bash host_network_test.sh 10.0.0.12
        主机网络状态信息
================================
10.0.0.12 网络状态: 正常
================================
[root@localhost ~]# /bin/bash host_network_test.sh 10.0.0.15
        主机网络状态信息
================================
10.0.0.15 网络状态: 异常
================================

字符串表达式

基础知识

简介

	所谓的字符串表达式,主要是判断 比较运算符 两侧的值的内容是否一致,由于bash属于弱类型语言,所以,默认情况下,无论数字和字符,都会可以被当成字符串进行判断。

符号解读

内容比较判断
    str1 == str2			str1和str2字符串内容一致
    str1 != str2			str1和str2字符串内容不一致,!表示相反的意思

内容空值判断
    -z 	str					空值判断,获取字符串长度,长度为0,返回True
    -n  "str"				非空值判断,获取字符串长度,长度不为0,返回True
    						注意:str外侧必须携带"",否则无法判断

简单实践

实践1-内容比较判断

判断字符串内容是否一致
[root@localhost ~]# test aaa == bbb
[root@localhost ~]# echo $?
1
[root@localhost ~]# test aaa != bbb
[root@localhost ~]# echo $?
0

判断数字内容是否一致
[root@localhost ~]# num1=234 num2=456
[root@localhost ~]# test $num1 == $num2
[root@localhost ~]# echo $?
1
[root@localhost ~]# test $num1 != $num2
[root@localhost ~]# echo $?
0

image

实践2-空值判断

判断内容是否为空
[root@localhost ~]# string=nihao
[root@localhost ~]# test -z $string
[root@localhost ~]# echo $?
1
[root@localhost ~]# test -z $string1
[root@localhost ~]# echo $?
0

判断内容是否为不空,可以理解为变量是否被设置
[root@localhost ~]# unset str
[root@localhost ~]# [ -n $str ]
[root@localhost ~]# echo $?
0
[root@localhost ~]# [ -n "$str" ]
[root@localhost ~]# echo $?
1
[root@localhost ~]# str=value
[root@localhost ~]# [ -n "$str" ]
[root@localhost ~]# echo $?
0

image

实践3-脚本实践

查看脚本内容
[root@localhost ~]# cat simple_login_string.sh
#!/bin/bash
# 功能: 模拟shell登录
# 版本:v0.1
# 作者:书记
# 联系:www.superopsmsb.com

# 定制命令变量
OS_INFO=$(cat /etc/redhat-release)
KERNEL_INFO=$(uname -r)
OS_ARCH=$(uname -m)
HOSTNAME=$(hostname)

# 清屏
clear

# 输出提示信息
echo -e "\e[32m${OS_INFO} \e[0m"
echo -e "\e[32mKernel ${KERNEL_INFO} on an ${OS_ARCH} \e[0m"
echo "---------------------------------"
# 交互输入登陆信息
read -p "请输入用户名:" account
[ -z $account ] && read -p "请输入用户名:" account
read -s -t30 -p "请输入登录密码:" password
echo
echo "---------------------------------"
# 输出用户输入信息
[ $account == 'root' ] && [ $password == '123456' ] && echo "登录成功" || echo "登录失败"

image

脚本执行测试
[root@localhost ~]# /bin/bash simple_login_string.sh
CentOS Linux release 7.9.2009 (Core)
Kernel 3.10.0-1160.el7.x86_64 on an x86_64
---------------------------------
请输入用户名:root
请输入登录密码:
---------------------------------
登录成功
[root@localhost ~]# /bin/bash simple_login_string.sh
CentOS Linux release 7.9.2009 (Core)
Kernel 3.10.0-1160.el7.x86_64 on an x86_64
---------------------------------
请输入用户名:
请输入用户名:root1
请输入登录密码:
---------------------------------
登录失败
[root@localhost ~]#

image

文件表达式

基础知识

简介

	所谓的文件表达式,主要是判断文件相关的权限和属性信息的。

表达式解读

文件属性判断
	-d  检查文件是否存在且为目录文件
    -f  检查文件是否存在且为普通文件
    -S	检查文件是否存在且为socket文件
    -L	检查文件是否存在且为链接文件
    -O  检查文件是否存在并且被当前用户拥有
    -G  检查文件是否存在并且默认组为当前用户组
    
文件权限判断   
    -r  检查文件是否存在且可读
    -w  检查文件是否存在且可写
    -x  检查文件是否存在且可执行
    
文件存在判断
    -e  检查文件是否存在
    -s  检查文件是否存在且不为空
    
文件新旧判断
    file1 -nt file2  检查file1是否比file2新
    file1 -ot file2  检查file1是否比file2旧
    file1 -ef file2  检查file1是否与file2是同一个文件,判定依据的是i节点

简单实践

实践1- 文件属性判断

[root@localhost ~]# [ -f weizhi.sh ] && echo "是一个文件"
[root@localhost ~]# [ -f weizhi.sddh ] || echo "不是一个文件"
不是一个文件
[root@localhost ~]# [ -d weizhi.sddh ] || echo "不是一个目录"
不是一个目录
[root@localhost ~]# [ -d /tmp ] && echo "是一个目录"
是一个目录

image

实践2-文件权限判断

[root@localhost ~]# [ -x memory_info.sh ] || echo "文件没有执行权限"
文件没有执行权限
[root@localhost ~]# [ -x memory_info.sh ] || chmod +x memory_info.sh
[root@localhost ~]# [ -x memory_info.sh ] && ./memory_info.sh
        localhost 内存使用信息统计
==========================================
内存总量:      3770
内存使用量:    242
内存空闲量:    3372
内存使用比率:  6.41
内存空闲利率:  89.44
==========================================

实践3-文件存在判断

文件内容空值判断
[root@localhost ~]# touch nihao.txt
[root@localhost ~]# [ -s nihao.txt ] || echo "文件为空"
文件为空
[root@localhost ~]# echo nihao > file.txt
[root@localhost ~]# [ -s file.txt ] && echo "文件不为空"
文件不为空

image

文件存在与否判断
[root@localhost ~]# [ -e file.txt ] && echo "文件存在"
文件存在
[root@localhost ~]# [ -e file.txt1 ] || echo "文件不存在"
文件不存在

image

数字表达式

基础知识

简介

主要根据给定的两个值,判断第一个与第二个数的关系,如是否大于、小于、等于第二个数。

语法解读

    n1 -eq n2   相等		n1 -ne n2   不等于		n1 -ge n2   大于等于	
    n1 -gt n2   大于		n1 -lt n2   小于		n1 -le n2   小于等于

简单实践

实践1-命令实践

[root@localhost ~]# [ 3 -gt 2 ] && echo "3 大于 2"
3 大于 2
[root@localhost ~]# [ 3 -ne 2 ] && echo "3 不等于 2"
3 不等于 2
[root@localhost ~]# [ 3 -eq 3 ] && echo "3 等于 3"
3 等于 3

image

实践2-脚本安全

查看脚本内容
[root@localhost ~]# cat test_argnum.sh
#!/bin/bash
# -eq 和 -ne 演示

# 设定普通变量
arg_num=$#

[ $arg_num -eq 1 ] && echo "脚本参数为1,允许执行脚本"
[ $arg_num -ne 1 ] && echo "脚本参数不为1,不允许执行脚本"
脚本执行效果
root@localhost ~]# /bin/bash test_argnum.sh
脚本参数不为1,不允许执行脚本
[root@localhost ~]# /bin/bash test_argnum.sh 1
脚本参数为1,允许执行脚本
[root@localhost ~]# /bin/bash test_argnum.sh 1 2
脚本参数不为1,不允许执行脚本

表达式进阶

[[]]

基础知识

简介

	我们之前学习过 test 和 [ ] 测试表达式,这些简单的测试表达式,仅仅支持单条件的测试。如果需要针对多条件测试场景的话,我们就需要学习[[  ]] 测试表达式了。
	我们可以将 [[  ]] 理解为增强版的 [ ],它不仅仅支持多表达式,还支持扩展正则表达式和通配符。

语法解析

基本格式:
	[[ 源内容 操作符 匹配内容 ]]
	
操作符解析:
	== 左侧源内容可以被右侧表达式精确匹配
	=~ 左侧源内容可以被右侧表达式模糊匹配

简单实践

实践1-内容的基本匹配

定制默认的的变量
[root@localhost ~]# string=value
[root@localhost ~]# echo $string
value
[root@localhost ~]# [[ $string == value ]]
[root@localhost ~]# echo $?
0

[[]] 支持通配符
[root@localhost ~]# [[ $string == v* ]]
[root@localhost ~]# echo $?
0

使用""取消正则,则内容匹配失败
[root@localhost ~]# [[ $string == v"*" ]]
[root@localhost ~]# echo $?
1

使用\取消正则,则内容匹配失败
[root@localhost ~]# [[ $string == v\* ]]
[root@localhost ~]# echo $?
1

image

实践2-文件的匹配

定制文件名称
[root@localhost ~]# script_name=file.sh
[root@localhost ~]# [[ $script_name == *.sh ]]
[root@localhost ~]# echo $?
0

尝试其他匹配
[root@localhost ~]# [[ $script_name == *.txt ]]
[root@localhost ~]# echo $?
1
[root@localhost ~]# [[ $script_name != *.txt ]]
[root@localhost ~]# echo $?
0

image

集合基础

基础知识

简介

	所谓的集合,主要是针对多个条件表达式组合后的结果,尤其是针对于逻辑场景的组合。初中数学的相关逻辑示意图:

image

表现样式

两个条件
	1 - 真 		0 - 假
三种情况:
	与 - &		或 - |		非 - !
	
注意:
	这里的 0 和 1 ,千万不要与条件表达式的状态值混淆
与关系:
	0 与 0 = 0 		0 & 0 = 0
	0 与 1 = 0 		0 & 1 = 0
	1 与 0 = 0 		1 & 0 = 0
	1 与 1 = 1		1 & 1 = 1
或关系:
    0 或 0 = 0		0 | 0 = 0
    0 或 1 = 1		0 | 1 = 1
    1 或 0 = 1		1 | 0 = 1
    1 或 1 = 1		1 | 1 = 1
非关系:
    非 1 = 0   		! true = false
    非 0 = 1 		! false = true

简单实践

实践1- 简单判断

或实践
[root@localhost ~]# echo $[ 0 | 1 ]
1
[root@localhost ~]# echo $[ 0 | 0 ]
0
[root@localhost ~]# echo $[ 1 | 0 ]
1
[root@localhost ~]# echo $[ 1 | 1 ]
1
与实践
[root@localhost ~]# echo $[ 1 & 1 ]
1
[root@localhost ~]# echo $[ 1 & 0 ]
0
[root@localhost ~]# echo $[ 0 & 1 ]
0
[root@localhost ~]# echo $[ 0 & 0 ]
0
非实践
[root@localhost ~]# true
[root@localhost ~]# echo $?
0
[root@localhost ~]# false
[root@localhost ~]# echo $?
1

[root@localhost ~]# echo $[ ! 0 ]
1
[root@localhost ~]# echo $[ ! 1 ]
0

image

逻辑组合

基础知识

简介

所谓的条件组合,指的是在同一个场景下的多个条件的综合判断效果。

语法解析

方法1:
	[ 条件1 -a 条件2 ]		- 两个条件都为真,整体为真,否则为假
	[ 条件1 -o 条件2 ]		- 两个条件都为假,整体为假,否则为真
方法2:
	[[ 条件1 && 条件2 ]]	- 两个条件都为真,整体为真,否则为假
	[[ 条件1 || 条件2 ]]	- 两个条件都为假,整体为假,否则为真

简单实践

实践1-[]组合实践

[root@localhost ~]# user=root pass=123456
[root@localhost ~]# [ $user == "root" -a $pass == "123456" ]
[root@localhost ~]# echo $?
0
[root@localhost ~]# [ $user == "root" -a $pass == "1234567" ]
[root@localhost ~]# echo $?
1
[root@localhost ~]# [ $user == "root" -o $pass == "1234567" ]
[root@localhost ~]# echo $?
0
[root@localhost ~]# [ $user == "root1" -o $pass == "1234567" ]
[root@localhost ~]# echo $?
1

image

实践2 - [[]]组合实践

[root@localhost ~]# [[ $user == "root" && $pass == "123456" ]]
[root@localhost ~]# echo $?
0
[root@localhost ~]# [[ $user == "root" && $pass == "1234567" ]]
[root@localhost ~]# echo $?
1
[root@localhost ~]# [[ $user == "root" || $pass == "1234567" ]]
[root@localhost ~]# echo $?
0
[root@localhost ~]# [[ $user == "root1" || $pass == "1234567" ]]
[root@localhost ~]# echo $?
1

image

综合实践

堡垒机登录

脚本功能-扩充用户名和密码验证功能

[root@localhost ~]# cat simple_jumpserver.sh
#!/bin/bash
# 功能:定制堡垒机的展示页面
# 版本:v0.3
# 作者:书记
# 联系:www.superopsmsb.com

# 定制普通变量
login_user='root'
login_pass='123456'

# 堡垒机的信息提示
echo -e "\e[31m \t\t 欢迎使用堡垒机"
echo -e "\e[32m
-----------请选择你要登录的远程主机-----------
 1: 10.0.0.14 (nginx)
 2: 10.0.0.15 (tomcat)
 3: 10.0.0.19 (apache)
 q: 使用本地主机
----------------------------------------------
"'\033[0m'

# 由于暂时没有学习条件判断,所以暂时选择 q
read -p "请输入您要选择的远程主机编号: " host_index
read -p "请输入登录本地主机的用户名: " user
read -s -p "请输入登录本地主机的密码: " password
echo
# 远程连接主机
[[ ${user} == ${login_user} && ${password} == ${login_pass} ]] && echo "主机登录验证成功" || echo "您输入的用户名或密码有误"
脚本执行效果
[root@localhost ~]# /bin/bash simple_jumpserver.sh
                 欢迎使用堡垒机

-----------请选择你要登录的远程主机-----------
 1: 10.0.0.14 (nginx)
 2: 10.0.0.15 (tomcat)
 3: 10.0.0.19 (apache)
 q: 使用本地主机
----------------------------------------------

请输入您要选择的远程主机编号: q
请输入登录本地主机的用户名: root
请输入登录本地主机的密码:
主机登录验证成功
[root@localhost ~]# /bin/bash simple_jumpserver.sh
                 欢迎使用堡垒机

-----------请选择你要登录的远程主机-----------
 1: 10.0.0.14 (nginx)
 2: 10.0.0.15 (tomcat)
 3: 10.0.0.19 (apache)
 q: 使用本地主机
----------------------------------------------

请输入您要选择的远程主机编号: q
请输入登录本地主机的用户名: python
请输入登录本地主机的密码:
您输入的用户名或密码有误

信息检测

脚本功能-检测公司网站的存活

判断网站的命令
[root@localhost ~]# wget --spider -T5 -q -t2 www.baidu.com
[root@localhost ~]# echo $?
0
[root@localhost ~]# wget --spider -T5 -q -t2 www.baidu.com1
[root@localhost ~]# echo $?
4
[root@localhost ~]# curl -s -o /dev/null www.baidu.com
[root@localhost ~]# echo $?
0
[root@localhost ~]# curl -s -o /dev/null www.baidu.com1
[root@localhost ~]# echo $?
6
脚本的核心内容
[root@localhost ~]# cat site_healthcheck.sh
#!/bin/bash
# 功能:定制站点的检测功能
# 版本:v0.1
# 作者:书记
# 联系:www.superopsmsb.com

# 定制普通变量
site_addr="$1"
# 脚本基本判断
[ -z ${site_addr} ] && echo "请输入待测试站点域名" && exit
[ $# -ne 1 ] && echo "请保证输入1个脚本参数" && exit

# 检测平台的信息提示
echo -e "\e[32m-----------检测平台支持的检测类型-----------
 1: wget
 2: curl
----------------------------------------"'\033[0m'

# 选择检测类型
read -p "请输入网站的检测方法: " check_type
site_status=$([ ${check_type} == 1 ] && wget --spider -T5 -q -t2 ${site_addr} && echo "正常" || echo "异常") 
site_status=$([ ${check_type} == 2 ] && curl -s -o /dev/null ${site_addr} && echo "正常" || echo "异常")
# 信息输出
echo
echo -e "\e[31m\t站点状态信息\e[0m"
echo -e "\e[32m================================"
echo "${site_addr} 站点状态: ${site_status}"
echo -e "================================\e[0m"
脚本执行效果
[root@localhost ~]# /bin/bash site_healthcheck.sh
请输入待测试站点域名
[root@localhost ~]# /bin/bash site_healthcheck.sh aa bb
请保证输入1个脚本参数
[root@localhost ~]# /bin/bash site_healthcheck.sh www.baidu.com
-----------检测平台支持的检测类型-----------
 1: wget
 2: curl
----------------------------------------
请输入网站的检测方法: 1

        站点状态信息
================================
www.baidu.com 站点状态: 异常
================================
[root@localhost ~]# /bin/bash site_healthcheck.sh www.baidu.com1
-----------检测平台支持的检测类型-----------
 1: wget
 2: curl
----------------------------------------
请输入网站的检测方法: 2

        站点状态信息
================================
www.baidu.com1 站点状态: 异常
================================

数组

数组基础

基础知识

简介

	数组(Array)是有序的元素序列,它是数据结构在shell当中非常常见的一种数据存储方式,它将有限个类型相同的数据放到一个集合中,这个集合就是数组。
	为了操作方便,我们为数组定制一个名称变量,数组内的每一个数据都是数组元素,这些数组元素在集合中有顺序的观念,顺序的位置值我们称为下标。

image

数组分类

数组样式-从数据结构的本身出发,它主要有多种数组样式

一维数组
	一维数组是最简单的数组,按照顺序将一系列数据排列下来即可,数组本身没有场景含义。
	数据的表现样式:数组[下标]
	适用场景:编程语言中基于数据的查询、聚合等操作
二维数组
	二维数组是业务场景中常见的数组表现样式,即在一维数组的前提下,扩充了数据元素在场景中的含义。
	数据的表现样式:数组[行下标][列下标]
	适用场景:数据库场景中基于数据的查询、聚合等操作
三维数组
	三维数组是大型业务场景中通用的一种数组表现样式,它是在二维数据的前提下,扩充了数据空间的含义。
	数据的表现样式:数组[行下标][列下标][页下标]
	适用场景:数据分析场景中基于数据的查询、聚合等操作

image

注意:
	1 bash支持一维数组(不支持多维数组),并且没有限定数组的大小。数组元素的下标由0开始编号。
	2 获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于0
	3 bash的数组支持稀疏格式(索引名称可以不连续)

数组定义

基础知识

数组创建

在Shell中,用括号来表示数组,数组元素用“空格”符号分割开。定义数组的语法格式:
    array_name=(value1 ... valuen)
注意:
	基于元素的格式,主要有单行定义、多行定义、单元素定义、命令定义等多种样式

语法解读

单行定义
	array_name=(value0 value1 value2 value3)
	
多行定义
    array_name=(
    value0
    value1
    value2
    value3
    )
    
单元素定义
    array_name[0]=value0
    array_name[1]=value1
    array_name[2]=value2
    
注意:
	单元素定义的时候,可以不使用连续的下标,而且下标的范围没有限制。
	
命令定义就是value的值以命令方式来获取
	file_array=($(ls /tmp/))

简单实践

实践1-单行定义

定制数据数组
[root@localhost ~]# num_list=(123,234,345,456,567)
[root@localhost ~]# echo ${num_list[0]}
123,234,345,456,567

数据元素之间使用空格隔开
[root@localhost ~]# num_list=(123 234 345 456 567)
[root@localhost ~]# echo ${num_list[0]}
123
[root@localhost ~]# echo ${num_list[@]}
123 234 345 456 567

image

实践2-多行定义

定制数组
[root@localhost ~]# class_one=(
> zhangsan
> lisi
> wangwu
> zhaoliu
> )

查看数组元素
[root@localhost ~]# echo ${class_one[0]}
zhangsan
[root@localhost ~]# echo ${class_one[@]}
zhangsan lisi wangwu zhaoliu

image

实践3-单元素定义

定制数组
[root@localhost ~]# mix_list[0]=nihao
[root@localhost ~]# mix_list[2]=345
[root@localhost ~]# mix_list[4]="1.23,4.56"

查看数组元素
[root@localhost ~]# echo ${mix_list[1]}
[root@localhost ~]# echo ${mix_list[@]}
nihao 345 1.23,4.56

批量多元素定义
[root@localhost ~]# string_list=([0]="value-1" [3]="value-2")
[root@localhost ~]# echo ${string_list[@]}
value-1 value-2
[root@localhost ~]# echo ${!string_list[@]}
0 3

image

实践4-命令定义

定制数组元素
[root@localhost ~]# file_array=$(ls *.sh)

查看数组元素
[root@localhost ~]# echo ${file_array[0]}
count_head_feet.sh host_network_test.sh memory_info.sh simple_jumpserver.sh simple_login.sh simple_login_string.sh site_healthcheck.sh test_argnum.sh
[root@localhost ~]# echo ${file_array[1]}
[root@localhost ~]# echo ${file_array[2]}
[root@localhost ~]# echo ${file_array[@]}
count_head_feet.sh host_network_test.sh memory_info.sh simple_jumpserver.sh simple_login.sh simple_login_string.sh site_healthcheck.sh test_argnum.sh

注意:
	对于命令的数组创建来说,它只有一个元素

数组取值

基础知识

简介

	对于shell的数组数据来说,获取制定的数组元素主要有两种方法,一种是获取内容,一种是获取其他信息。

语法解读

基于索引找内容
	读取数组元素值可以根据元素的下标值来获取,语法格式如下:
    	${array_name[index]}
    	${array_name[@]:起始位置:获取数量}
注意:
	获取具体的元素内容,指定其下标值,从0开始
	获取所有的元素内容,下标位置写"@"或者"*"
获取数组索引
	在找内容的时候,有时候不知道数组的索引都有哪些,我们可以基于如下方式来获取,数组的所有索引:
    	${!array_name[index]}
注意:
	获取所有的元素位置,下标位置写"@"或者"*"
获取数组长度的方法与获取字符串长度的方法相同,格式如下:
	${#array_name[index]}
注意:
	获取具体的元素长度,指定其下标值,从0开始
	获取所有的元素个数,下标位置写"@"或者"*"
从系统中获取所有的数组
	declare -a

简单实践

实践1-基于索引找内容

设定数组内容
[root@localhost ~]# num_list=(123 234 345 456 567)

获取指定位置元素
[root@localhost ~]# echo ${num_list[0]}
123
[root@localhost ~]# echo ${num_list[1]}
234

获取所有位置元素
[root@localhost ~]# echo ${num_list[*]}
123 234 345 456 567
[root@localhost ~]# echo ${num_list[@]}
123 234 345 456 567

获取末尾位置元素
[root@localhost ~]# echo ${num_list[-1]}
567
[root@localhost ~]# echo ${num_list[-2]}
456

获取指定范围元素
[root@localhost ~]# echo ${num_list[@]:1:1}
234
[root@localhost ~]# echo ${num_list[@]:1:3}
234 345 456

image

实践2-基于内容获取元素

[root@localhost ~]# echo ${!num_list[@]}
0 1 2 3 4
[root@localhost ~]# echo ${!num_list[@]}
0 1 2 3 4

image

实践3-获取数组长度

获取数组的元素数量
[root@localhost ~]# echo ${#num_list[@]}
5
[root@localhost ~]# echo ${#num_list[*]}
5

获取数据元素的长度
[root@localhost ~]# echo ${#num_list[3]}
3

image

实践4-获取系统所有数组

设定数组
[root@localhost ~]# num_list=(123 234 345 456 567)

查看所有数组
[root@localhost ~]# declare -a
declare -a BASH_ARGC='()'
declare -a BASH_ARGV='()'
declare -a BASH_LINENO='()'
declare -a BASH_SOURCE='()'
declare -ar BASH_VERSINFO='([0]="4" [1]="2" [2]="46" [3]="2" [4]="release" [5]="x86_64-redhat-linux-gnu")'
declare -a DIRSTACK='()'
declare -a FUNCNAME='()'
declare -a GROUPS='()'
declare -a PIPESTATUS='([0]="0")'
declare -a num_list='([0]="123" [1]="234" [2]="345" [3]="456" [4]="567")'

数组变动

元素修改

简介

数组元素的改其实就是定义数组时候的单元素定义,主要包含两种,元素替换,元素部分内容替换,格式如下
元素内容替换:
	array_name[index]=值
注意:
	在修改元素的时候,index的值一定要保持准确
元素部分内容替换,可以参考字符串替换格式:
	${array_name[index]/原内容/新内容}
注意:
	默认是演示效果,原数组未被修改,如果真要更改需要结合单元素内容替换

简单实践

修改指定位置的值
[root@localhost ~]# num_list[2]=aaa
[root@localhost ~]# echo ${num_list[@]}
123 234 aaa 456 567

替换元素值的特定内容
[root@localhost ~]# echo ${num_list[2]/aa/lualu-}
lualu-a
[root@localhost ~]# num_list[2]=${num_list[2]/aa/lualu-}
[root@localhost ~]# echo ${num_list[@]}
123 234 lualu-a 456 567

image

元素删除

简介

将shell中的数组删除,可以使用unset来实现,主要有两种情况:删除单元素,删除整个数组。格式如下:
删除单元素
	unset array_name[index]
删除整个数组
	unset array_name

简单实践

删除指定的元素
[root@localhost ~]# echo ${num_list[@]}
123 234 lualu-a 456 567
[root@localhost ~]# unset num_list[2]
[root@localhost ~]# echo ${num_list[@]}
123 234 456 567
[root@localhost ~]# unset num_list[2]
[root@localhost ~]# echo ${num_list[@]}
123 234 456 567
[root@localhost ~]# unset num_list[1]
[root@localhost ~]# echo ${num_list[@]}
123 456 567
[root@localhost ~]# echo ${!num_list[@]}
0 3 4
替换元素值的特定内容
[root@localhost ~]# unset num_list
[root@localhost ~]# echo ${!num_list[@]}

[root@localhost ~]#

image
image

综合实践

数组关联

基础知识

简介

	上一节,我们学习了shell环境下的数组定制的简写方式。数组的定制主要有如下两种:
定制索引数组 - 数组的索引是普通的数字
	declare -a array_name
	- 普通数组可以不事先声明,直接使用
	
定制关联数组 - 数组的索引是自定义的字母
	declare -A array_name
	- 关联数组必须先声明,再使用

简单实践

实践1-定制索引数组

定制一个空内容的数组
[root@localhost ~]# declare -a course
[root@localhost ~]# declare -a | grep course
declare -a course='()'

定制一个包含元素的数组
[root@localhost ~]# course=(yuwen shuxue yingyu)
[root@localhost ~]# declare -a | grep course
declare -a course='([0]="yuwen" [1]="shuxue" [2]="yingyu")'

image

实践2-定制关联数组

定制关联数组
[root@localhost ~]# declare -A score
[root@localhost ~]# declare -a | grep score
[root@localhost ~]# declare -A | grep score
declare -A score='()'

关联数组和数字索引数组不能通用
[root@localhost ~]# declare -a score
-bash: declare: score: 无法将关联数组转化为索引数组
关联数组的操作
[root@localhost ~]# declare -A | grep score
declare -A score='([yingyu]="32" [yuwen]="67" [shuxue]="65" )'
[root@localhost ~]# echo ${!score[@]}
yingyu yuwen shuxue
[root@localhost ~]# echo ${score[@]}
32 67 65

image

数组案例

信息统计

需求

分别打印CPU 1min 5min 15min load负载值
命令提示:
	uptime
	
信息显示:
    CPU 1 min平均负载为: 0.00
    CPU 5 min平均负载为: 0.01
    CPU 15 min平均负载为: 0.05

编写脚本

查看脚本内容
[root@localhost ~]# cat cpu_load.sh
#!/bin/bash
# 功能:采集系统cpu负载信息
# 版本:v0.1
# 作者:书记
# 联系:www.superopsmsb.com

# 获取CPU负载情况
cpu_load=($(uptime | tr -s " " | cut -d " " -f 11-13 | tr "," " "))

# 信息输出
echo -e "\e[31m\t系统cpu负载信息\e[0m"
echo -e "\e[32m================================"
echo "CPU 1 min平均负载为: ${cpu_load[0]}"
echo "CPU 5 min平均负载为: ${cpu_load[1]}"
echo "CPU 15 min平均负载为: ${cpu_load[2]}"
echo -e "================================\e[0m"
脚本执行后效果
[root@localhost ~]# /bin/bash cpu_load.sh
        系统cpu负载信息
================================
CPU 1 min平均负载为: 0.00
CPU 5 min平均负载为: 0.01
CPU 15 min平均负载为: 0.05
================================

服务管理

需求

服务的管理动作有:
	"启动" "关闭" "重启" "重载" "状态"
服务的管理命令有:
	"start" "stop" "restart" "reload" "status"
选择不同的动作,输出不同的服务执行命令,格式如下:
	systemctl xxx service_name

编写脚本

[root@localhost ~]# cat service_manager.sh
#!/bin/bash
# 功能:定制服务管理的功能
# 版本:v0.1
# 作者:书记
# 联系:www.superopsmsb.com

# 定制普通数组
oper_array=(启动 关闭 重启 重载 状态)
# 定制关联数组
declare -A cmd_array
cmd_array=([启动]=start [关闭]=stop [重启]=restart [重载]=reload [状态]=status)

# 服务的操作提示
echo -e "\e[31m---------------服务的操作动作---------------
 1: 启动  2: 关闭  3: 重启  4: 重载  5: 状态
--------------------------------------------"'\033[0m'

# 选择服务操作类型
read -p "> 请输入服务的操作动作: " oper_num
echo 
echo -e "\e[31m您选择的服务操作动作是:  ${oper_array[$oper_num-1]}\e[0m"
echo -e "\e[32m===============服务的执行动作===============
您即将对服务执行如下命令:
\tsystemctl ${cmd_array[${oper_array[$oper_num-1]}]} service_name
=========================================="'\033[0m'
脚本执行效果
[root@localhost ~]# /bin/bash service_manager.sh
---------------服务的操作动作---------------
 1: 启动  2: 关闭  3: 重启  4: 重载  5: 状态
--------------------------------------------
> 请输入服务的操作动作: 3

您选择的服务操作动作是:  重启
===============服务的执行动作===============
您即将对服务执行如下命令:
        systemctl restart service_name
==========================================
posted @ 2023-01-16 22:28  aBiu--  阅读(48)  评论(0编辑  收藏  举报