Linux中shell编程表达式和数组讲解

一、表达式

1.1 测试表达式

复制代码
样式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

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

1.2 逻辑表达式

语法解读

复制代码
&&		
	示例:命令1  &&  命令2
		如果命令1执行成功,那么我才执行命令2		-- 夫唱妇随
		如果命令1执行失败,那么命令2也不执行

||	
	示例:命令1 || 命令2
		如果命令1执行成功,那么命令2不执行			-- 对着干
		如果命令1执行失败,那么命令2执行

!
	示例:! 命令
		如果命令执行成功,则整体取反状态

实践1:

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

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

实践2-案例实践

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

# 设定普通变量
arg_num=$#

[ $# == 1 ] && echo "脚本参数为1,允许执行脚本"
[ $# == 1 ] || echo "脚本参数不为1,不允许执行脚本"

实践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

实践4 - 组合使用

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

1.3 字符串表达式

复制代码
内容比较判断
    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

实践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

1.4 文件表达式

表达式解读

复制代码
文件属性判断
	-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 "是一个目录"
是一个目录

实践2-文件权限判断

复制代码
#!/bin/bash
# 功能: 统计内存使用率信息

# 定制普通变量
tmp_file='/tmp/mem.txt'

# 设定内存的基本信息
free -m > /tmp/mem.txt 2>&1
mem_total=$(grep Mem /tmp/mem.txt | tr -s " " | cut -d " " -f2)
mem_used=$(grep Mem /tmp/mem.txt | tr -s " " | cut -d " " -f3)
mem_free=$(grep Mem /tmp/mem.txt | tr -s " " | cut -d " " -f4)

# 统计内存的使用率
percentage_used=$(echo "scale=2; ${mem_used} / ${mem_total} * 100" | bc)
percentage_free=$(echo "scale=2; ${mem_free} / ${mem_total} * 100" | bc)

# 信息的显示
echo -e "\e[31m\t $(hostname) 内存使用信息统计\e[0m"
echo "----------------------------------------"
echo -e "\e[32m内存总量: ${mem_total}
内存使用量: ${mem_used}
内存空闲量: ${mem_free}
内存使用占比: ${percentage_used}
内存空闲占比: ${percentage_free}\e[0m"
echo "----------------------------------------"

1.5 数字表达式

语法解读

复制代码
    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

实践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,不允许执行脚本

1.6 集合基础

语法解析

复制代码
方法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

实践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

1.7 综合实践

跳板机登录

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

复制代码
[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
请输入登录本地主机的密码:
您输入的用户名或密码有误

二、数组基础

2.1 数组定义

语法解读

复制代码
单行定义
	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

实践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

实践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

2.2 数组取值

复制代码
从系统中获取所有的数组
	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

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

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

实践3-获取数组长度

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

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

实践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")'

2.3 数组变动

元素修改

简单实践

复制代码
修改指定位置的值
[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

元素删除

复制代码
删除单元素
	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 ~]#

三、综合实践

数组关联

复制代码
	上一节,我们学习了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")'

实践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

数组案例

需求:

复制代码
分别打印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 9-11 | 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
==========================================
相关推荐
面朝大海,春不暖,花不开7 分钟前
管理数据洪流:自动化处理与归档每日数据文件的策略与实践
运维·python·自动化
地衣君7 小时前
RISC-V 开发板 + Ubuntu 23.04 部署 open_vins 过程
linux·ubuntu·risc-v
5:007 小时前
云备份项目
linux·开发语言·c++
powerfulzyh8 小时前
非Root用户启动SSH服务经验小结
运维·ssh
云道轩8 小时前
升级centos 7.9内核到 5.4.x
linux·运维·centos
是小满满满满吗8 小时前
传输层:udp与tcp协议
linux·服务器·网络
爱学习的小道长8 小时前
Ubuntu Cursor升级成v1.0
linux·运维·ubuntu
EelBarb8 小时前
seafile:ubuntu搭建社区版seafile12.0
linux·运维·ubuntu
Xam_d_LM8 小时前
【Latex】Windows/Ubuntu 绘制 eps 矢量图通用方法(drawio),支持插入 Latex 数学公式
linux·ubuntu·科研·矢量图·drawio