Shell
shell 介绍
- 什么shell
- shell功能
一、什么是shell
shell是一个程序,采用C语言编写,是用户和linux内核沟通的桥梁。它既是一种命令语言,又是一种解释性的编程语言。通过一个图表来查看一下shell的作用。
作用:
kernel:为软件服务,接收用户或软件指令驱动硬件,完成工作
shell:命令解释器
user:用户接口,对接用户。
二、shell 功能
- 命令行解释功能
- 启动程序
- 输入输出重定向
- 管道连接
- 文件名置换 (echo /*)
- 变量维护
- 环境控制
- shell编程
shell 语法
- 如何抒写一个shell脚本
- shell脚本运行
- shell中的特殊符号
- 重定向
- shell中数学运算
shell脚本就是一组命令的集合,按照先后顺序执行;自上而下写入到一个文本文件中,然后给予执行权限。
一、如何抒写一个shell脚本
-
shell脚本的命名
- 名字要有意义,最好不要用a、b、c、d、1、2、3、4这种方式命名;虽然linux系统中,文件没有扩展名的概念,依然建议你用.sh结尾;名字不要太长,最好能在30个字节以内解决。例如:check_memory.sh
-
shell脚本格式
- shell脚本开头必须指定脚本运行环境 以 #!这个特殊符号组合来组成。如: #!/bin/bash 指定该脚本是运行解析由/bin/bash来完成的;
shell脚本中,最好加入脚本说明字段
#!/bin/bash
#Author: lemon
#Created Time: 2018/08/2712:27
#Script Description: first shell study script
#下面写执行的命令语句
。。。
。。。
。。。
二、如何运行一个shell脚本
[root@lemon ~]# cat /etc/shells #查看当前系统的所有shell环境
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
脚本运行需要执行权限,当我们给一个文件赋予执行权限后,该脚本就可以运行。
[root@lemon ~]# chmod u+x filename
如果不希望赋予脚本执行权限,那么可以使用bash命令来运行未给予执行权限的脚本bash fiename
[root@lemon ~]# bash filename
三、shell中的特殊符号
~ 家目录,cd ~ 代表进入用户家目录
! 执行历史命令,!! 执行上一条命令
$ 变量中取内容符
+ - * / % 对应数学运算 加 减 乘 除 取余
* 星号是shell中的通配符 匹配所有
? 问号是shell中的通配符 匹配除回车以外的一个字符
\ 转义字符
`` 反引号,命令中执行命令
' ' 强转义,禁止引用其他变量值,$视为普通字符;意思就是' '号里面写的是什么,显示的就是什么
" " 弱转义,允许通过$符号引用其他变量值;意思就是将" "号里面变量名的变量值显示出来
| 管道符,把前边命令的输出结果,作为后边命令的处理对象
|| 逻辑或,前边的命令执行成功,后面的不执行;前边的命令执行失败,后面的命令将会被执行
& 后台执行,仅在当前终端生效,断开终端后放在后台运行的进程也会随着退出
&& 逻辑与,只有前边的命令执行成功,才会执行后面的命令
; 和 && 符号不同的一点就是 他不管前面命令的结果是否成功,它都会执行后面命令
四、shell重定向
#默认是正确重定向
1> 重定向正确覆盖;把屏幕上的正确内容输出到文件
1>> 重定向正确追加;把屏幕上的正确内容以追加的形式输出到文件
2> 重定向错误覆盖;把屏幕上的错误内容输出到文件
2>> 重定向错误追加;把屏幕上的错误内容以追加的形式输出到文件
&> 重定向混合覆盖;把屏幕上无论是正确还是错误的都输出到文件
&>> 重定向混合追加;把屏幕上无论是正确还是错误以追加的形式都输出到文件
五、shell数学运算
#$双小圆括号运算,在shell中$(( ))也可以用来做数学运算(推荐)
[root@lemon ~]# echo $((100 + 3))
103
[root@lemon ~]# echo $((100 - 3))
97
[root@lemon ~]# echo $((100 * 3))
300
[root@lemon ~]# echo $((100 / 3))
33
[root@lemon ~]# echo $((100 % 3))
1
[root@lemon ~]# echo $((100 ** 3))
1000000
#expr 命令:只能做整数运算,格式比较古板,注意空格
[root@lemon ~]# expr 1 + 1
2
[root@lemon ~]# expr 5 - 2
3
[root@lemon ~]# expr 5 \* 2 #注意*出现应该转义,否则认为是通配符
10
[root@lemon ~]# expr 5 / 2
2
#使用bc计算器处理浮点运算,scale=2代表小数点保留两位
[root@lemon ~]# echo "scale=2; 100 + 3" | bc
103
[root@lemon ~]# echo "scale=2; 100 - 3" | bc
97
[root@lemon ~]# echo "scale=2; 100 * 3" | bc
300
[root@lemon ~]# echo "scale=2; 100 / 3" | bc
33.33
shell格式化输出
- echo 命令
- 颜色输出
一个程序需要有0个或以上输入,一个或更多输出
一、echo 命令介绍
-
功能:将内容输出到默认显示设备
- echo命令的功能是在显示器上显示一段文字,一般起到一个提示的作用。 功能说明:显示文字。
- 补充说明:echo会将输入的字符串送往标准输出。输出的字符串间以空白字符隔开,并在 最后加上换行号。
-
命令选项:
-
–help 显示帮助–version显示版本信息
-
-n 不要在最后自动换行
-
-e 若字符串中出现以下特殊字符,则特别加以处理,而不会将它当成一般文字输出:
- \t 插入tab;
- \n 换行且光标移至行首;
- \a 发出警告声;
- \c 忽略掉这个参数之后的任何字符信息;
- \r 输出回车后的内容信息;
- \v 换行但光标仍旧停留在原来的位置;与\f相同;
-
-
举例说明:
# \n
[root@lemon ~]# echo -e "lemon\nhandsome"
lemon
handsome
# \t
[root@lemon ~]# echo -e "lemon\thandsome"
lemon handsome
# \c
[root@lemon ~]# echo -e "lemon\chandsome"
lemon[root@lemon-min ~]#
# \r
[root@lemon ~]# echo -e "lemo\rnhandsome"
nhandsome
# \v
[root@lemon ~]# echo -e "lemon\vhandsome"
lemon
handsome
二、颜色代码
脚本中echo显示内容带颜色显示,echo显示带颜色,需要使用参数
-e
示例如下:
下面是相应的字和背景颜色,可以自己来尝试找出不同颜色搭配
echo -e "\033[31m 红色字 \033[0m"
echo -e "\033[34m 黄色字 \033[0m"
echo -e "\033[41;33m 红底黄字 \033[0m"
echo -e "\033[41;37m 红底白字 \033[0m"
字颜色:30—–37
echo -e "\033[30m 黑色字 \033[0m"
echo -e "\033[31m 红色字 \033[0m"
echo -e "\033[32m 绿色字 \033[0m"
echo -e "\033[33m 黄色字 \033[0m"
echo -e "\033[34m 蓝色字 \033[0m"
echo -e "\033[35m 紫色字 \033[0m"
echo -e "\033[36m 天蓝字 \033[0m"
echo -e "\033[37m 白色字 \033[0m"
字背景颜色范围:40—–47
echo -e "\033[40;37m 黑底白字 \033[0m"
echo -e "\033[41;37m 红底白字 \033[0m"
echo -e "\033[42;37m 绿底白字 \033[0m"
echo -e "\033[43;37m 黄底白字 \033[0m"
echo -e "\033[44;37m 蓝底白字 \033[0m"
echo -e "\033[45;37m 紫底白字 \033[0m"
echo -e "\033[46;37m 天蓝底白字 \033[0m"
echo -e "\033[47;30m 白底黑字 \033[0m"
最后面控制选项说明
\033[0m 关闭所有属性
\033[1m 设置高亮度
\033[4m 下划线
\033[5m 闪烁
\033[7m 反显
\033[8m 消隐
设置前景色
\033[40m — \33[47m 设置背景色
\033[nA 光标上移n行
\033[nB 光标下移n行
\033[nC 光标右移n行
\033[nD 光标左移n行
\033[y;xH设置光标位置
\033[2J 清屏
\033[K 清除从光标到行尾的内容
\33[s 保存光标位置
\033[u 恢复光标位置
\033[?25l 隐藏光标
\033[?25h 显示光标
shell交互输入定义变量
一、read基本命令
-
默认接受键盘的输入,回车符代表输入结束
-
read 命令选项:
- -p 打印信息
- -t 交互停留的时间
- -s 隐藏在外面输入值的信息
- -n 限制输入字符个数
二、read 基本示例
[root@lemon ~]# vim read.sh
#!/bin/bash
clear
echo "Centos linux 7 (Core)"
echo -e "Kernel `uname -r` an `uname -m`"
read -n 6 -p "login:" user
read -n 6 -t 10 -s -p "Passd:" passwd
echo
echo -e "login:$user\nPassd:$passwd"
# 执行此脚本
[root@lemon ~]# bash read.sh
Centos linux 7 (Core)
Kernel 3.10.0-693.el7.x86_64 an x86_64
login:lemon
Passd:
login:lemon
Passd:123123
变量
- 变量介绍
- 变量分类
- 变量管理
一、变量介绍
在编程中,我们总有一些数据需要临时存放在内存,以待后续使用时快速读出。内存在系统启动的时候被按照1B一个单位划分为若干个块,然后统一编号(16进制编号),并对内存的使用情况做记录,保存在内存跟踪表中。
- 那么问题来了,1G内存有多少个1B的单位呢?
计算机的单位:
1B=8bit
1KB=1024B
1MB=1024KB
1GB=1024MB
1TB=1024GB
1PB=1024TB
1EB=1024PB
1ZB=1024EB
...
好了,已经够大了!当然还有YB、BB更大的单位,同样进制也是1024.
1G=1024*1024*1024=1073741824B
假如你将一个1B的字符存入内存,如何读出呢?有没有一种大海捞针的感觉啊!我们讨论一下计算机是如何通过让我们快速将数据存在内存,如何从内存中读出数据的。我们研究过变量后就明白了。
-
变量:变量是编程中最常用的一种临时在内存中存取数据的一种方式。
-
变量存取原理
-
从图片可以看出,当我们在脚本中定义变量存值的时候,可以从以下方面看到变化:
-
内存占用:如果存的是一个字符则占用1个字节,如果存的是字符串则是字符串的长度加1个字节长度(\0是一个特殊字符,代表字符串结束)。
-
变量名与内存空间关系:计算机中会将对应的内存空间和变量名称绑定在一起,此时代表这段内存空间已经被程序占用,其他程序不可复用;然后将变量名对应的值存在对应内存地址的空间里。
-
# 理解变量存储:STRING1="ABC"
1)0X5...0X8(物理地址) <===> STRING1(逻辑地址) <===> 存取数据(ABC)
0x5...0x8是内存物理地址,是计算机寻址的依据;
STRING1是给人看的,方便人记忆;
2)对于人来说STRING1上存的数据是ABC,对于计算机来说数据是存在物理地址上的;
3)在建立变量的时候计算机自动将逻辑地址(变量名)和物理地址做了对应。
#变量读出
1)当调用STRING1的时候,计算机会根据对应关系,找到物理地址
2)定位内存地址,读出数据并返回
二、变量分类
注意事项:一定要按顺序写位置变量名!比如:$1 $2 $3 $4 $5 $6,如果位置变量用到两位数或多位数的话就用{}括上;比如:echo '$10 是' ${10};如果不加就会出现问题!
- 本地变量:用户私有变量,只有本用户可以使用,保存在家目录下的.bash_profile、.bashrc文件中
- 全局变量:所有用户都可以使用,保存在/etc/profile、/etc/bashrc文件,可使用
export
命令查看系统变量 - 用户自定义变量:用户自定义,比如脚本中的变量
- 位置变量:可以放在任何位置的变量,$1、$2、$3……
案例解释
# 准备脚本
[root@lemon ~]# vim envd.sh
#!/bin/bash
echo '我是$1:' $1
echo '我是$2:' $2
echo '我是$3:' $3
echo '我是$4:' $4
echo '我是$5:' $5
echo '我是$6:' $6
echo '我是$7:' $7
echo '我是$8:' $8
echo '我是$9:' $9
echo '我是$10:' ${10}
# 执行此脚本
[root@lemon ~]# bash envd.sh q w e r t y u i o p
我是$1: q
我是$2: w
我是$3: e
我是$4: r
我是$5: t
我是$6: y
我是$7: u
我是$8: i
我是$9: o
我是$10: p
- 预定义变量:$0、$#、$*、$?
-
$?:上一条命令执行后返回的状态,当返回状态值为0时表示执行正常,非0值表示执行异常或出错
-
$#:命令行中位置变量的个数
-
$0:当前执行的进程/程序名
-
$*:所有位置变量的内容,$@ 和 $*差不多。但是注意,他俩是有区别的!!!
- 使用如下的脚本查看 $@ 和 $* 的区别
-
# 准备脚本
[root@lemon ~]# vim envd.sh
#!/bin/bash
echo "我是-->$1"
echo "我是-->$2"
echo "我是-->$3"
echo '=================================='
for i in "$*";do
echo "$i"
done
echo '=================================='
for y in "$@";do
echo "$y"
done
echo '=================================='
# 执行脚本
[root@lemon ~]# sh envd.sh a1 a2 a3
我是-->a1
我是-->a2
我是-->a3
==================================
a1 a2 a3
==================================
a1
a2
a3
==================================
案例解释:
# 准备脚本
[root@lemon ~]# vim envd.sh
#!/bin/bash
echo "我是-->$1"
echo "我是-->$2"
echo "我是-->$3"
echo "$?"
echo "$#"
echo "$*"
echo "$0"
# 执行脚本
[root@lemon ~]# bash envd.sh a1 a2 a3
我是-->a1
我是-->a2
我是-->a3
0
3
a1 a2 a3
envd.sh
三、使用变量
1. 定义变量
-
变量格式: 变量名=值
-
在shell编程中的变量名和等号之间不能有空格。
-
变量名命名规则:
# 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
# 中间不能有空格,可以使用下划线(_)。
# 不能使用标点符号。
# 不能使用bash里的关键字(可用help命令查看保留关键字)。
- 定义变量演示:
# 变量赋值,此种方法设置为本地变量
[root@lemon ~]# name='lemon'
[root@lemon ~]# pass='12345'
2. 读取变量内容
- 读取方法:$变量名
# 变量内容读出
[root@lemon ~]# echo $name
lemon
[root@lemon ~]# echo $age
12345
3. 取消变量 unset
[root@lemon ~]# unset name
[root@lemon ~]# echo $name
4. 定义全局变量 export
[root@lemon ~]# export name='lemon'
- 上述设置的变量其实都是一次性的变量,系统重启就会丢失。
- 如果希望本地变量或者全局变量可以永久使用,可以将需要设置的变量写入变量文件中即可。
5. 定义永久变量
-
本地变量:用户私有变量,只有本用户可以使用,保存在家目录下的.bash_profile、.bashrc文件中
-
全局变量:所有用户都可以使用,保存在/etc/profile、/etc/bashrc文件中
# 本地变量
[root@lemon ~]# tail -1 .bash_profile
export name='lemon'
[root@lemon ~]# source .bash_profile
# 全局变量
[root@lemon ~]# tail -1 /etc/profile
export age=18
[root@lemon ~]# source /etc/profile
数组
- 数组介绍
- 基本数组
- 关联数组
- 案列分享
一、数组介绍
-
概述:数组就是一组数据类型相同集合
-
一个变量只能存一个值,但是现实中又很多值需要存储,那么变量就有些拘谨了。比如做一个学员信息表,一个班50个人,每个人6条信息,我们需要定义300个变量才能完成。恐怖恐怖,这只是一个班的学生,一个学校呢?一个市呢?......我想静静了!
-
仔细想想上述的案例,一个学生六个信息:ID、姓名、性别、年龄、成绩、班级。可不可以定义六个变量就能存储这六类信息呢?答案是当然可以的!但变量不行,我们可以用数组。
二、基本数组
数组可以让用户一次赋予多个值,需要读取数据时只需通过索引调用就可以方便读出了。
1. 数组语法
数组名称=() #定义空元素的数组
数组名称=(元素1 元素2 元素3 ...) #定义有元素的数组
2. 数组读出
${数组名称[索引]}
索引默认是元素在数组中的排队编号,默认第一个从0开始
3. 数组赋值
- 数组赋值格式:数组名[下标]=值,下标不存在,则新增数组元素; 下标已有,则覆盖值。
方法1:一次赋一个值
[root@lemon ~]# array1[0]='tom'
[root@lemon ~]# array1[1]='jarry'
方法2:一次赋多个值
[root@lemon ~]# array2=(tom jack alice "bash shell") #linux默认都是字符串类型数据
[root@lemon ~]# array3=(`cat /etc/passwd`) #希望是将该文件中的每一个行作为一 个元素赋值给数组array3
4. 查看基本数组-a
# 查看数组
[root@lemon ~]# declare -a
5. 访问数组元素
[root@lemon ~]# echo ${array1[0]} #访问数组中的第一个元素
[root@lemon ~]# echo ${array1[*]} #访问数组中所有元素 等同于 echo ${array1[@]}
[root@lemon ~]# echo ${#array1[*]} #统计数组元素的个数
[root@lemon ~]# echo ${!array2[*]} #获取数组元素中的所有索引
[root@lemon ~]# echo ${array1[*]:1} #从数组下标1开始
[root@lemon ~]# echo ${array1[*]:1:2} #从数组下标1开始,访问两个元素
6. 遍历数组
- 默认数组通过数组元素的个数进行遍历
[root@lemon ~]# for i in `echo ${array1[*]}`;do echo $i; done
pear
apple
orange
peach
三、关联数组
1. 定义关联数组
- 申明关联数组变量
[root@lemon ~]# declare -A ass_array1
[root@lemon ~]# declare -A ass_array2
2. 关联数组赋值
方法1:一次赋一个值
[root@lemon ~]# ass_array1[index1]=pear
[root@lemon ~]# ass_array1[index2]=apple
[root@lemon ~]# ass_array1[index3]=orange
[root@lemon ~]# ass_array1[index4]=peach
方法2:一次赋多个值
[root@lemon ~]# ass_array2=([index1]=tom [index2]=jack [index3]=alice [index4]='tool')
3. 查看关联数组-A
[root@lemon ~]# declare -A
4. 访问数组元素
@ 等同于 *
[root@lemon ~]# echo ${ass_array2[index2]} #访问数组中的第二个元数
[root@lemon ~]# echo ${ass_array2[*]} #访问数组中所有元数 等同于 echo ${array1[@]}
[root@lemon ~]# echo ${#ass_array2[*]} #获得数组元素的个数
[root@lemon ~]# echo ${!ass_array2[*]} #获得数组元素的全部索引
5. 遍历数组
[root@lemon ~]# for i in `echo ${ass_array2[*]}`;do echo $i; done
tool
tom
jack
alice
6. 简单的学员信息系统
#!/bin/bash
for ((i=0;i<3;i++));do
read -p "输入第$((i + 1))个人名: " name[$i]
read -p "输入第$[$i + 1]个年龄: " age[$i]
read -p "输入第`expr $i + 1`个性别: " gender[$i]
echo
done
clear
echo -e "\t\t\t\t\t\t学员查询系统"
while true;do
cp=0
read -p "输入要查询的姓名: " xm
[ $xm == "q" ] && exit
for ((i=0;i<3;i++));do
if [ "$xm" == "${name[$i]}" ];then
echo -e "name: ${name[$i]}\nage: ${age[$i]}\ngender: ${gender[$i]}"
cp=1
fi
done
[ $cp -eq 0 ]&&echo "not found student"
done
shell流程控制-if判断语句
- shell中的四大运算
- if语法
- 在写程序的时候,时常会对上一步执行是否成功如何判断苦恼,当我们今天学习了if就可以解决你的苦恼。
- if语句在我们程序中就是用来做判断的,不管学习的是什么语言,以后只要涉及到判断的部分,都可以直接拿if来使用,不同的语言之间的if只是语法不同,原理是相同的。
一、shell中的运算
1. 数学比较运算
- 示例:
-eq #等于
[root@lemon ~]# [ 9 -eq 10 ] ; echo $?
1
[root@lemon ~]# [ 10 -eq 10 ] ; echo $?
0
-gt #大于
[root@lemon ~]# [ 10 -gt 10 ] ; echo $?
1
[root@lemon ~]# [ 11 -gt 10 ] ; echo $?
0
-lt #小于
[root@lemon ~]# [ 9 -lt 10 ] ; echo $?
0
[root@lemon ~]# [ 10 -lt 10 ] ; echo $?
1
-ge #大于或等于
[root@lemon ~]# [ 9 -ge 10 ] ; echo $?
1
[root@lemon ~]# [ 11 -ge 10 ] ; echo $?
0
[root@lemon ~]# [ 10 -ge 10 ] ; echo $?
0
-le #小于或等于
[root@lemon ~]# [ 9 -le 10 ] ; echo $?
0
[root@lemon ~]# [ 10 -le 10 ] ; echo $?
0
[root@lemon ~]# [ 11 -le 10 ] ; echo $?
1
-ne #不等于
[root@lemon ~]# [ 9 -ne 10 ] ; echo $?
0
[root@lemon ~]# [ 10 -ne 10 ] ; echo $?
1
[root@lemon ~]# [ 11 -ne 10 ] ; echo $?
0
2. 字符串比较运算
-
注意字符串,一定别忘了使用引号引起来
-
示例:
== #等于,也可以指写一个 = 号
[root@lemon ~]# [ 'lemon' == 'lemon' ] ; echo $?
0
[root@lemon ~]# [ 'lemon' == 'tom' ] ; echo $?
1
!= #不等于
[root@lemon ~]# [ 'lemon' != 'lemon' ] ; echo $?
1
[root@lemon ~]# [ 'lemon' != 'tom' ] ; echo $?
0
-z #检查字符串的长度是否为空,加上!号就是取反 ! -z
[root@lemon ~]# echo "$name"
lemon
[root@lemon ~]# echo "$named"
[root@lemon ~]# [ -z $name ] ; echo $?
1
[root@lemon ~]# [ -z $named ] ; echo $?
0
[root@lemon ~]# [ ! -z $name ] ; echo $?
0
[root@lemon ~]# [ ! -z $named ] ; echo $?
1
3. 文件比较与检查
- 示例:
-e #检查目录及文件是否存在
[root@lemon ~]# [ -e /etc ] ; echo $?
0
[root@lemon ~]# [ -e /etc/passwd ] ; echo $?
0
[root@lemon ~]# [ -e /etcc ] ; echo $?
1
[root@lemon ~]# [ -e /etcc/passwd ] ; echo $?
1
-d #检查文件是否存在且为目录
[root@lemon ~]# [ -d /etc ] ; echo $?
0
[root@lemon ~]# [ -d /tom ] ; echo $?
1
-f #检查文件是否存在且为文件
[root@lemon ~]# [ -f /etc/passwd ] ; echo $?
0
[root@lemon ~]# [ -f /etc/file ] ; echo $?
1
-nt #检查file1是否比file2新,按照时间比对的
[root@lemon opt]# ll -h *
-rw-r--r-- 1 root root 0 6月 5 21:21 lemon.txt
-rw-r--r-- 1 root root 0 6月 5 21:28 tools.txt
[root@lemon opt]# [ lemon.txt -nt tools.txt ] ; echo $?
1
[root@lemon opt]# [ tools.txt -nt lemon.txt ] ; echo $?
0
-ot #检查file1是否比file2旧,按照时间比对的
[root@lemon opt]# ll -h *
-rw-r--r-- 1 root root 0 6月 5 21:21 lemon.txt
-rw-r--r-- 1 root root 0 6月 5 21:28 tools.txt
[root@lemon opt]# [ lemon.txt -ot tools.txt ] ; echo $?
0
[root@lemon opt]# [ tools.txt -ot lemon.txt ] ; echo $?
1
-s #检查目录及文件是否存在且不为空
[root@lemon ~]# [ -s /etc/passwd ] ; echo $?
0
[root@lemon ~]# [ -s /etc ] ; echo $?
0
[root@lemon ~]# [ -s /etcc/passwd ] ; echo $?
1
[root@lemon ~]# [ -s /etcc ] ; echo $?
1
-O #检查文件是否存在并且被当前用户拥有
测试省略……
-G #检查文件是否存在并且默认组为当前用户组
测试省略……
-r #检查文件是否存在且可读
测试省略……
-w #检查文件是否存在且可写
测试省略……
-x #检查文件是否存在且可执行
测试省略……
4. 逻辑运算
- 逻辑运算注意事项:
- 逻辑与 或 运算都需要两个或以上条件,逻辑非运算只能一个条件。
- 口诀:
- 逻辑与运算 <==> 真真为真 真假为假 假假为假
- 逻辑或运算 <==> 真真为真 真假为真 假假为假
- 逻辑非运算 <==> 真为假 假为真
&& #逻辑与运算
[root@lemon ~]# [ 10 -eq 10 ] && [ 'lemon' == 'lemon' ] ; echo $?
0
[root@lemon ~]# [ 10 -eq 20 ] && [ 'lemon' == 'lemon' ] ; echo $?
1
|| #逻辑或运算
[root@lemon ~]# [ 10 -eq 10 ] || [ 'lemon' == 'lemon' ] ; echo $?
0
[root@lemon ~]# [ 10 -eq 20 ] || [ 'lemon' == 'lemon' ] ; echo $?
0
[root@lemon ~]# [ 10 -eq 20 ] || [ 'lemon' == 'tomcc' ] ; echo $?
1
! #逻辑非运算
[root@lemon ~]# [ ! 10 -eq 10 ] ; echo $?
1
[root@lemon ~]# [ ! 10 -eq 11 ] ; echo $?
0
二、if 语法
1. 语法一: 单if语句
-
适用范围:只需要一步判断,条件返回真干什么或者条件返回假干什么。
-
语句格式:
if [ condition ];then #condition 值为true or false
commands
fi
- 该语句翻译成汉语大意如下:
如果条件为真
那么
执行commands代码块
结束
- 通过一段代码来演示一下
[root@lemon ~]# vim if.sh
#!/bin/bash
if [ 10 -eq 10 ];then
echo "hello world"
fi
- 执行结果
[root@lemon ~]# bash if.sh
hello world
2. 语法二: if-else语句
- 适用范围:两步判断,条件为真干什么,条件为假干什么。
if [ condition ];then
commands1
else
commands2
fi
- 该语句翻译成汉语大意如下:
如果条件为真
那么
执行commands1代码块
否则
执行commands2代码块
结束
- 通过一段代码演示一下
[root@lemon ~]# vim if.sh
#!/bin/bash
read -p "请输入数字:" number
if [ $number -eq 10 ];then
echo "条件为真"
else
echo "条件为假"
fi
- 执行结果
[root@lemon ~]# bash if.sh
请输入数字:10
条件为真
[root@lemon ~]# bash if.sh
请输入数字:20
条件为假
3. 语法三: if-elif-else语句
- 适用范围:多于两个以上的判断结果,也就是多于一个以上的判断条件。
if [ condition 1 ];then
commands1
elif [ condition 2 ];then
commands2
.......
else
commandsX
fi
- 该语句翻译成汉语大意如下:
如果 条件1 为真
那么
执行代码块1
如果 条件2 为真
那么
执行代码块2
以此类推的N个条件及对应的执行代码块
否则 【以上所有条件中没有一个满足的】
执行代码块X
结束
- 通过一段代码演示一下
[root@lemon ~]# vim if.sh
#!/bin/bash
read -p "请输入数字:" number
if [ $number -eq 10 ];then
echo "条件 1 为真"
elif [ $number -gt 10 ];then
echo "条件 2 为真"
else
echo "以上条件都为假"
fi
- 执行结果
[root@lemon ~]# bash if.sh
请输入数字:9
以上条件都为假
[root@lemon ~]# bash if.sh
请输入数字:10
条件 1 为真
[root@lemon ~]# bash if.sh
请输入数字:11
条件 2 为真
三、if 高级应用
循环脚本控制语句
特殊条件表达式
true #当条件表达式为true时,那么代表条件表达式永远成立,为真; 【条件成立】
false #当条件表达式为false时,那么代表条件表达式永远不成立,为假;【条件不成立】
测试 continue、break、exit 语句的不同点
通俗点讲:break是立马跳出循环;continue是跳出当前条件循环,继续下一轮条件循环;exit是直接退出整个脚本
- 代码如下
[root@lemon ~]# vim test.sh
#!/bin/bash
for i in {1..5};do
if [ $i -eq 3 ];then
$1
fi
echo "$i"
done
echo "循环体外命令"
- 执行结果
# continue
[root@lemon ~]# bash test.sh continue
1
2
4
5
循环体外命令
# break
[root@lemon ~]# bash test.sh break
1
2
循环体外命令
# exit
[root@lemon ~]# bash test.sh exit
1
2
1. sleep N 脚本执行到该步休眠N秒
-
作用:执行完上一条命令要停留的时间
-
段代如下
[root@lemon ~]# vim for.sh
#!/bin/bash
for i in `seq 9 -1 1`;do
echo -n -e "倒计时: $i\r"
sleep 1
done
- 执行结果
2. continue 跳过循环中的某次循环
-
作用:终止本次循环,继续执行下一次的循环
-
代码如下,默认循环输出1-9,但是使用continue跳过输出5
[root@lemon ~]# vim for.sh #!/bin/bashfor i in {1..9};do if [ $i -eq 5 ];then continue else echo "$i" fidone
- 执行结果
[root@lemon ~]# bash for.sh 12346789
3. break 跳出循环继续执行后续代码
-
作用:退出脚本中当前的循环体,继续执行循环体外的命令
-
代码如下,默认循环输出1-9,当输出到5的时候跳出循环
#!/bin/bash
for i in {1..9};do
if [ $i -eq 6 ];then
break
else
echo "$i"
fi
done
- 执行结果
[root@lemon ~]# bash for.sh
1
2
3
4
5
- break N 跳出某个循环,用在嵌套循环环境,从内循环到外循环依次编号为1-N
[root@lemon ~]# vim for.sh
#!/bin/bash
#脚本分三步执行
#1.没有break 那么脚本i=1的时候 内循环将无限循环,满屏幕haha
#2.内循环加break 终止内循环 内循环只执行一次 输出就会loop 1 haha loop2 haha ....
#3.内循环加break 2 终止外循环 输出loop 1 haha 脚本运行结束
for (( i=1; i<5; i++ ));do
echo "#loop $i"
for ((;;)); do
echo "haha"
break 2
done
sleep 3
done
- 执行结果
[root@lemon ~]# bash for.sh
#loop 1
haha
4. exit 退出当前脚本
-
作用:直接退出脚本,循环体外的命令不会执行
-
代码如下
[root@lemon ~]# vim for.sh
#!/bin/bash
for i in {1..9};do
if [ $i == 5 ];then
exit 2 #后面的数字就是一个返回值,可用echo $?查看
else
echo "$i"
fi
done
- 执行结果
[root@lemon ~]# bash for.sh
1
2
3
4
[root@lemon ~]# echo $?
2
shell流程控制-for循环语句
- for循环介绍
- for语法
- 循环控制
脚本在执行任务的时候,总会遇到需要循环执行的时候,比如说我们需要脚本每隔五分钟执行一次ping的操作,除了计划任务,我们还可以使用脚本来完成,那么我们就用到了循环语句。
循环的优点:
- 节省内存 10M脚本 1M脚本 哪个更剩内存 完成同一个任务
- 结构更清晰
- 节省开发时间成本
一、for 循环介绍
- for的工作原理
很多人把for循环叫做条件循环,或者for i in 。其实前者说的就是for的特性,for循环的次数和给予的条件是成正比的,也就是你给5个条件,那么他就循环5次;后者说的是for的语法。
二、for语法
1. for 语法一
for i in value1 value2 ......;do
commands
done
- 接下来看段代码吧,循环输出1-9数字
[root@lemon ~]# vim for.sh
#!/bin/bash
for i in `seq 1 9`;do
echo "number <==> $i"
done
- 代码执行结果:
[root@lemon ~]# bash for.sh
number <==> 1
number <==> 2
number <==> 3
number <==> 4
number <==> 5
number <==> 6
number <==> 7
number <==> 8
number <==> 9
2. for语法二
for ((变量;条件;自增减运算));do
代码块
done
来段代码理解一下,看下还是输出1-9
[root@lemon ~]# vim for.sh
#!/bin/bash
for ((i=1;$i<10;i++));do
echo "number <==> $i"
done
- 代码执行结果
[root@lemon ~]# bash for.sh
number <==> 1
number <==> 2
number <==> 3
number <==> 4
number <==> 5
number <==> 6
number <==> 7
number <==> 8
number <==> 9
for循环使用多个变量
[root@lemon ~]# vim for.sh
#!/bin/bash
for ((a=0,b=9;$a<10;a++,b--));do
echo "number <==> $a $b"
done
- 代码执行结果
[root@lemon ~]# bash for.sh
number <==> 0 9
number <==> 1 8
number <==> 2 7
number <==> 3 6
number <==> 4 5
number <==> 5 4
number <==> 6 3
number <==> 7 2
number <==> 8 1
number <==> 9 0
for 无限循环 使用((;😉) 条件可以实现无线循环
- 代码如下
[root@lemon ~]# vim for.sh
#!/bin/bash
for ((;;));do
echo "hello world"
done
- 执行结果
[root@lemon ~]# bash for.sh
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
……………………………
shell流程控制-while循环语句
- while循环介绍
- while循环语法
- while实战
一、while循环介绍
- while的工作原理
当程序执行到while语句时,首先会计算condition的值,如果condition的值为假,结束整个while语句,如果表达式的值为真,则执行语句,执行完语句再去计算表达式的值,如果表达式的值还为真,则在执行语句.....直到表达式的值为假才会停止,否则一直循环。
二、while循环语法
while [ condition ];do #注意,条件为真while才会循环,条件为假,while停止循环
commands
done
三、while实战
1. 跳板机脚本
#!/bin/bash
lemon(){
contiue=$(pgrep -l -u jump|grep "bash"|awk '{print $1}')
kill -9 $contiue
}
trap "" HUP INT QUIT TSTP
while true
do
read -p "andmin:
1).input is sshd server:192.168.1.1;
2).input is sshd server:192.168.1.2;
3).Input to user exit's 192.168.1.3;
what is to name's server:" num
if [[ $num -eq 1 ]];then
ssh root@192.168.1.1
elif [[ $num -eq 2 ]];then
ssh root@192.168.1.2
elif [[ $num -eq 3 ]];then
lemon
else
echo "$0语法|1|2|3"
sleep 2
fi
done
2. 遍历文件内容
- 代码如下
[root@lemon ~]# vim while.sh
#!/bin/bash
while read i;do
echo "$i"
done < $1
- 执行结果
[root@lemon ~]# bash while.sh /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:997:User for polkitd:/:/sbin/nologin
tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
3. 读出文件中的列,IFS指定默认的列分隔符
- 代码如下
[root@lemon ~]# vim while.sh
#!/bin/bash
IFS=$":"
while read f1 f2 f3 f4 f5 f6 f7;do
echo "$f1 $f2 $f3"
done < /etc/passwd
- 执行结果
[root@lemon ~]# bash while.sh
root x 0
bin x 1
daemon x 2
adm x 3
lp x 4
sync x 5
shutdown x 6
halt x 7
mail x 8
operator x 11
games x 12
ftp x 14
nobody x 99
systemd-network x 192
dbus x 81
polkitd x 999
postfix x 89
chrony x 998
sshd x 74
4. 批量创建用户 及 设置密码
#!/bin/bash
password=$(echo $RANDOM|md5sum|cut -c 1-6)
number=100
for user in lemon{1..100};do
useradd $user
while [ $number -ge 1 ];do
echo $password|passwd --stdin $user &>/dev/null
let number--
if [ $? -eq 0 ];then
tail -100 /etc/passwd
else
echo "error"
fi
done
done
until语句
- until介绍
- until语法
- 案例分享
一、until介绍
和 while正好相反,until是条件为假开始执行,条件为真停止执行。
二、until语法
until [ condition ];do #注意,条件为假until才会循环,条件为真,until停止循环
commands代码块
done
三、案例
- 代码如下
[root@lemon ~]# vim until1.sh
#打印10-20数字
#!/bin/bash
init_num=10
until [ $init_num -gt 20 ]
do
echo $init_num
init_num=$((init_num + 1))
done
- 执行结果
[root@lemon ~]# sh until1.sh
10
11
12
13
14
15
16
17
18
19
20
case多条件分支语句
- case介绍
- case语法
- shell特殊变量
一、case介绍
- 概述:根据变量的值,顺序匹配模式,匹配后执行命令并结束,如果没有匹配的模式,则执行默认命令,执行成功后退出,返回1,然后退出case
- 意思就是:下面的模式就是变量名的值,模式里面的命令都会执行;从第一个开始,直到最后一个结束;如果没有模式则默认执行exit退出脚本命令
二、case语法
case 变量 in
模式1)
执行代码块1
;;
模式2)
执行代码块2
;;
......
*) 代表不是以上模式的值就执行 *)
执行的默认代码块;默认是exit。
;;
esac
注意:每个代码块执行完毕要以;;结尾代表结束,case结尾要以倒过来写的esac来结束。
- 代码如下
[root@lemon ~]# vim case.sh
#!/bin/bash
case $1 in
1)
echo haha
;;
2)
echo hehe
;;
3)
echo heihei
;;
*)
echo 'usage:|1|2|3|'
;;
esac
- 执行结果
[root@lemon ~]# bash case.sh 1
haha
[root@lemon ~]# bash case.sh 2
hehe
[root@lemon ~]# bash case.sh 3
heihei
[root@lemon ~]# bash case.sh 4
usage:|1|2|3|
[root@lemon ~]# bash case.sh 4asd
usage:|1|2|3|
三、shell特殊变量
$* #代表所有参数,其间隔为IFS内定参数的第一个字元
$@ #与*星号类同。不同之处在於不参照IFS
$# #代表参数数量
$ #执行上一个指令的返回值
$- #最近执行的foreground pipeline的选项参数
$$ #本身的Process ID
$ #执行上一个背景指令的PID
$_ #显示出最後一个执行的命令
$N #shell的第几个外传参数
shell函数
- 函数介绍
- 函数语法
- 函数应用
一、函数介绍
Shell函数类似于Shell脚本里面存放了一系列的指令,不过Shell的函数存在于内存,而不是硬盘文件,所以速度很快,另外,Shell还能对函数进行预处理,所以函数的启动比脚本更快。
shell允许将一组命令集 或 语句形成一个可用块,这些块称为shell函数。
说白了,函数就是将某一组shell命令封装成一个模块,想用的时候就用,不用就放在原地
函数的优点:
- 代码模块化,调用方便,节省内存
- 代码模块化,代码量少,排错简单
- 代码模块化,可以改变代码的执行顺序
二、函数的语法
语法一:
函数名 () {
代码块
[return N]
}
语法二:
function 函数名 {
代码块
[return N]
}
解析:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。函数中的关键字“return”可以放到函数体的任意位置,通常用于返回某些值,Shell在执行到return之后,就停止往下执行,返回到主程序的调用行,return的返回值只能是0~256之间的一个整数,返回值将保存到变量“$?”中。
三、Shell函数的退出及删除
-
函数结束之后会返回调用函数的部分继续执行
-
退出函数体:exit 退出整个脚本、break跳出当前函数体
-
shell中查询函数及删除:
source 或 . 脚本 #将函数载入到内存
declare -f #可以显示定义的函数内容
declare -F #可以只显示定义的函数名
unset -f 函数名 #可以从Shell内存中删除函数
四、函数的变量概念
- 全局变量:默认情况下,脚本中定义的任何变量都是全局变量,在函数外定义的变量可在函数内正常访问。
- 局部变量:函数内部使用全局变量可以用 'local 变量名=值' 声明成局部变量,局部变量只能在函数体内生效。
五、函数的应用
- 定义一个函数
print () {
echo "lemon"
或者
function hello {
echo "hello world"
}
print 和 hello就是函数的名字,函数名字命名参考变量一节中的变量命名规则
定义好函数后,如果想调用该函数,只需通过函数名调用即可
- 代码如下
[root@lemon ~]# vim function.sh
#!/bin/bash
N1 () {
echo "world"
}
N2 () {
echo "hello"
}
N3 () {
echo "lemon is a very good"
}
#方便调整代码执行顺序
N2
N1
#可以重复调用摸个函数
N3
N3
- 执行结果
[root@lemon ~]# bash function.sh
hello
world
lemon is a very good
lemon is a very good
自动化交互 expect 详解
一、expect介绍
- expect是一个自动化交互套件,主要应用于执行命令和程序时,系统以交互形式要求输入指定字符串,实现交互通信。
- expect自动交互流程:spawn启动指定进程 <---> expect获取指定关键字 <---> send向指定程序发送指定字符 <---> 执行完成退出
- 使用场景:在管理系统的多种情况下,会出现管理员手动输入字符,称为交互式,如下:
二、安装 expect 工具
# 安装 expect 工具
[root@lemon ~]# yum -y install expect
# 查看 expect 版本
[root@lemon ~]# expect -v
expect version 5.45
三、expect常用命令
spawn #交互程序开始后面跟命令或者指定程序
expect #获取匹配信息匹配成功则执行expect后面的程序动作; expect eof 代表正常结束
send exp_send #用于发送指定的字符串信息
exp_continue #在expect中多次匹配就需要用到
send_user #用来打印输出 相当于shell中的echo
exit #退出expect脚本
eof #expect执行结束 退出
set #定义变量
puts #输出变量
set timeout #设置超时时间
interact #允许用户交互; 停留在远程终端
四、expect 基本语法
spawn linux命令
expect "关键字"
expect {
"*yes/no" { send "yes\r"; exp_continue}
"*password:" { send "$password\r" }
}
interact
五、expect 应用案例
案例1:expect自动交互ssh远程登录
-
需要两台虚拟机
- 主机一 :IP --- 192.168.2.1 主机名 --- lemon
- 主机二 :IP --- 192.168.2.2 主机名 --- docker
-
代码如下
[root@lemon ~]# which expect #查看expect程序位置
/usr/bin/expect
[root@lemon ~]# vim ssh.exp #后缀名是什么都行
#!/usr/bin/expect
# set设置变量,将第一个位置变量交给变量ip
set ip [lindex $argv 0]
# set设置变量,将第二个位置变量交给变量password
set password [lindex $argv 1]
# spawn指定命令
spawn ssh root@$ip
# expect指定匹配字段
expect {
# exp_continue表示继续匹配字段, send自动输入指定交互式内容
"*(yes/no)?" { send "yes\r"; exp_continue }
"*password:" { send "$password\r" }
}
# Interact开启交互式模式,不然导致远程后无法敲命令
interact
- 执行结果
[root@lemon ~]# chmod u+x ssh.exp #给予执行权限
[root@lemon ~]# rm -rf .ssh/known_hosts #删除之前的key验证文件,不删也可以
[root@lemon ~]# ./ssh.exp 192.168.2.2 123123 #执行脚本
spawn ssh root@192.168.2.2
The authenticity of host '192.168.2.2 (192.168.2.2)' can't be established.
ECDSA key fingerprint is SHA256:dSDdVALO14Vgdmr49RwTkRDyXrZHmCz1CUVlSf0fZrM.
ECDSA key fingerprint is MD5:8e:75:d7:bc:9c:61:45:fc:0c:67:3b:3a:f6:db:fe:3c.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.2.2' (ECDSA) to the list of known hosts.
root@192.168.2.2's password:
Last login: Sat Jun 6 13:15:44 2020 from 192.168.2.1
[root@docker ~]#
[root@docker ~]# ifconfig ens32
ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.2.2 netmask 255.255.255.0 broadcast 192.168.2.255
inet6 fe80::20c:29ff:fe16:9fdd prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:16:9f:dd txqueuelen 1000 (Ethernet)
RX packets 349 bytes 46800 (45.7 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 262 bytes 43941 (42.9 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@docker ~]# exit
登出
Connection to 192.168.2.2 closed.
[root@lemon ~]#
案例2:expect自动交互式管理执行命令
-
需要两台虚拟机
- 主机一 :IP --- 192.168.2.1 主机名 --- lemon
- 主机二 :IP --- 192.168.2.2 主机名 --- docker
-
代码如下
[root@lemon ~]# vim admin.exp
#!/usr/bin/expect
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
set order [lindex $argv 3]
set timeout 10
spawn ssh $user@$ip $order
expect {
"*(yes/no)?" { send "yes\r"; exp_continue }
"*password:" { send "$password\r" }
}
interact
- 执行结果
[root@lemon ~]# chmod u+x admin.exp
[root@lemon ~]# ./admin.exp 192.168.2.2 root 123123 "free -m"
spawn ssh root@192.168.2.2 free -m
root@192.168.2.2's password:
total used free shared buff/cache available
Mem: 976 145 572 6 258 643
Swap: 1023 0 1023
[root@lemon ~]# ./admin.exp 192.168.2.2 root 123123 "df -hT"
spawn ssh root@192.168.2.2 df -hT
root@192.168.2.2's password:
文件系统 类型 容量 已用 可用 已用% 挂载点
/dev/mapper/centos-root xfs 18G 3.2G 15G 18% /
devtmpfs devtmpfs 478M 0 478M 0% /dev
tmpfs tmpfs 489M 0 489M 0% /dev/shm
tmpfs tmpfs 489M 6.7M 482M 2% /run
tmpfs tmpfs 489M 0 489M 0% /sys/fs/cgroup
/dev/sr0 iso9660 4.3G 4.3G 0 100% /mnt
/dev/sdb1 xfs 80G 33M 80G 1% /sdb1
/dev/sda1 xfs 1014M 125M 890M 13% /boot
tmpfs tmpfs 98M 0 98M 0% /run/user/0
案例3:expect自动化下发脚本,执行脚本(结合案例2)
-
需要两台虚拟机
- 主机一 :IP --- 192.168.2.1 主机名 --- lemon
- 主机二 :IP --- 192.168.2.2 主机名 --- docker
-
代码如下
# 准备要下发的脚本文件
[root@lemon ~]# vim ping.sh
#!/bin/bash
ping -c 3 127.0.0.1
# 准备远程下发脚本文件的脚本
[root@lemon ~]# vim remote_copy.exp
#!/usr/bin/expect
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
set sfile [lindex $argv 3]
set ddir [lindex $argv 4]
set timeout 10
spawn scp $sfile $user@$ip:$ddir
expect {
"*yes/no" { send "yes\r"; exp_continue}
"*password:" { send "$password\r" }
}
interact
- 执行结果
[root@lemon ~]# chmod a+x ping.sh
[root@lemon ~]# chmod u+x remote_copy.exp
# 下发脚本文件
[root@lemon ~]# ./remote_copy.exp 192.168.2.2 root 123123 /root/ping.sh /tmp/
spawn scp /root/ping.sh root@192.168.2.2:/tmp/
root@192.168.2.2's password:
ping.sh 100% 34 13.8KB/s 00:00
# 使用刚刚写好的admin.exp运行刚才remote_copy.exp下发给2.2主机上的shell脚本ping.sh
[root@lemon ~]# ./admin.exp 192.168.2.2 root 123123 "/tmp/ping.sh"
spawn ssh root@192.168.2.2 /tmp/ping.sh
root@192.168.2.2's password:
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.020 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.037 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.027 ms
--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2005ms
rtt min/avg/max/mdev = 0.020/0.028/0.037/0.007 ms
案例4:expect 变量的练习
- 代码如下
[root@lemon ~]# vim env.exp
#!/usr/bin/expect
# A=$1
set A [lindex $argv 0]
# B=$2
set B [lindex $argv 1]
# C=$3
set C [lindex $argv 2]
# D=lemon
set D lemon
send "\$A=$A\n"
send "\$B=$B\n"
send "\$C=$C\n"
send "\$D=$D\n"
# 相当$*
send "\$argv=$argv\n"
# 相当$#
send "\$argc=$argc\n"
- 执行结果
[root@lemon ~]# chmod u+x env.exp
[root@lemon ~]# ./env.exp a b c d
$A=a
$B=b
$C=c
$D=lemon
$argv=a b c d
$argc=4
案例5:expect 结合 if 的练习,
注意:括号的空格,严格要求
- 代码如下
[root@lemon ~]# vim if.exp
#!/usr/bin/expect
if {$argc != 3} {
# send:相当于echo
# send_user:相当于echo -e
send_user "usage: == $argc 不等于3!\n"
exit
} else {
set FILE [lindex $argv 0]
set HOST [lindex $argv 1]
set DIR [lindex $argv 2]
send_user "$FILE\t$HOST\t$DIR\n" }
- 执行结果
[root@lemon ~]# chmod u+x if.exp
[root@lemon ~]# ./if.exp a b
usage: == 2 不等于3!
[root@lemon ~]# ./if.exp a b c
a b c
Linux 信号控制
shell案例
一、监控主机及联网状态
1. 监控目的
- 掌握在线业务机器及联网的状态。
2. 监控方法
- 通过采用 ICMP协议的 ping命令对计算机进行 ping测试,通则表示主机为开启并联网,不通则代表主机宕机或断网。
3. 监控思路
- 通过分析多次 ping结果判断主机状态
4. 监控实现
#!/bin/bash
#Description:
#Author:
#Created Time:
#监控目标主机状态
#监控方法 ping ICMP协议
#ping 通 host up
#ping 不通 host down
#问题
#1、关于禁ping 防止DDOS
# 禁的是陌生人 禁止所有 允许你的IP
#满足条件
#网络有延迟 what's up 假报警问题
# ping 的取值 报警阈值 3次全部失败 报警机器down
#ping 的频率 秒级 5秒 or 1秒
#main
for ((i=1;i<4;i++));do
#测试代码
if ping -c1 $1 &>/dev/null;then
export ping_count"$i"=1
else
export ping_count"$i"=0
fi
#时间间隔
sleep 0.3
done
#3次ping失败报警
if [ $ping_count1 -eq $ping_count2 ] && [ $ping_count2 -eq $ping_count3 ]&&[ $ping_count1 -eq 0 ];then
echo "$1 is down"
else
echo "$1 is up"
fi
unset ping_count1
unset ping_count2
unset ping_count3
二、监控主机服务状态
1. 监控目的
实时掌握线上机器服务状态,保证服务正常运行
2. 监控方法
采用telnet访问端口,通过返回数据分析判定结果
3. 监控实现
#!/bin/bash
#Description:
#Author:
#Created Time:
#监控一个服务端口
#监控方法
#1)通过systemctl service 服务启动状态
#2)lsof 查看端口是否存在
#3)查看进程是否存在
######压力过大 无法响应 | 服务down了 上述东西还在
#4)测试端口是否有响应 推荐
#telnet 协议
#main
port_status () {
temp_file=`mktemp port_status.XXX`
#1、判断依赖命令telnet是否存在
[ ! -x /usr/bin/telnet ]&&echo "telnet: not found command"&& exit 1
#2、测试端口 $1 IP $2 port
( telnet $1 $2 <<EOF
quit
EOF
) &>$temp_file
#3、分析文件中的内容,判断结果
if egrep "\^]" $temp_file &>/dev/null;then
echo "$1 $2 is open"
else
echo "$1 $2 is close"
fi
rm -f $temp_file
}
#函数带参的问题
port_status $1 $2
三、监控内存使用率
1. 监控目的
通过监控内存使用率判定机器内存资源消耗情况,及时处理并优化资源配比
2. 监控方法
2.1)监控命令
- free
- cat /proc/meminfo
- 等等……
2.2)监控指标
- 内存使用率
- buffer & cache
2.3)监控方法
通过上述命令对内存总量、使用量进行截取,取得内存使用率,并根据阈值进行判断。
3. 监控实现
#!/bin/bash
#Description:
#Author:
#Created Time:
#内存使用率统计脚本
#/proc/meminfo
#内存申请顺序 free-cache-buffer-swap
memory_use() {
memory_used=`head -2 /proc/meminfo |awk 'NR==1{t=$2}NR==2{f=$2;print(t-f)*100/t"%"}'`
memory_cache=`head -5 /proc/meminfo |awk 'NR==1{t=$2}NR==5{c=$2;print c*100/t"%"}'`
memory_buffer=`head -4 /proc/meminfo |awk 'NR==1{t=$2}NR==4{b=$2;print b*100/t"%"}'`
echo -e "memory_used:$memory_used\tbuffer:$memory_buffer\tcached:$memory_cache"
}
memory_use
四、监控使用CPU或内存前十名进程
1. 监控目的
- 掌握系统进程对系统资源的使用情况,掌握机器的动态。
2. 监控方法
2.1)监控命令
-
ps
-
top
-
等等……
2.2)监控方法
- 通过对任务管理器中的进程对内存或CPU的使用情况进行整合、排序得出结论
3. 监控实现
#!/bin/bash
#Description:
#Author:
#Created Time:
#统计使用内存和CPU前十名进程
#统计系统中前十名使用内存最多的进程
memory() {
#1、收集任务管理器进程信息
temp_file=`mktemp memory.XXX`
top -b -n 1 > $temp_file
#2、按进程统计内存使用大小
tail -n +8 $temp_file | awk '{array[$NF]+=$6}END{for (i in array) print array[i],i}' |sort -k 1 -n -r|head -10
rm -f $temp_file
}
#统计系统中前十名使用CPU最多的进程
cpu() {
#1、收集任务管理器进程信息
temp_file=`mktemp memory.XXX`
top -b -n 1 > $temp_file
#2、按进程统计内存使用大小
tail -n +8 $temp_file | awk '{array[$NF]+=$9}END{for (i in array) print array[i],i}' |sort -k 1 -n -r|head -10
rm -f $temp_file
}
echo memory
memory
echo cpu
cpu
五、监控IO使用情况
1. 磁盘说明
磁盘在系统中负责存储和读取任务,磁盘的处理速度直接影响了计算机的速度,目前常用的磁盘有两种:固态和机械磁盘。
- 固态磁盘:没有IO瓶颈,读写快,存储颗粒有擦写限制,价格高,容量小。
- 机械磁盘:靠电机带动磁盘转动,通过磁头读取或存储数据,读写速度和磁盘转速有关,容量大、价格低,大量读写容易造成IO瓶颈。
2. 监控目的
随时掌握IO的使用情况,防止IO性能瓶颈
3. 监控指标
- IO队列长度
- IOPS
- 磁盘吞吐量
4. 监控命令
-
iostat(sysstat包提供),本节重点讲解
-
iostat(sysstat包提供)重点讲解
-
iozone(三方提供)
5. iostat语法 及 输出说明
-
用法:
- iostat [ 选项 ] [ <时间间隔> [ <次数> ]]
-
常用选项:
- -c:只显示系统CPU统计信息,即单独输出avg-cpu结果,不包括device结果
- -d:单独输出Device结果,不包括cpu结果
- -k/-m:输出结果以kB/mB为单位,而不是以扇区数为单位
- -x:输出更详细的io设备统计信息
- interval/count:每次输出间隔时间,count表示输出次数,不带count表示循环输出
- 输出含义:
- avg-cpu: 总体cpu使用情况统计信息,对于多核cpu,这里为所有cpu的平均值。重点关注iowait值,表示CPU用于等待io请求的完成时间。
- %user:CPU处在用户模式下的时间百分比。
- %nice:CPU处在带NICE值的用户模式下的时间百分比。
- %system:CPU处在系统模式下的时间百分比。
- %iowait:CPU等待输入输出完成时间的百分比。
- %steal:管理程序维护另一个虚拟处理器时,虚拟CPU的无意识等待时间百分比。
- %idle:CPU空闲时间百分比。
- Device: 各磁盘设备的IO统计信息。各列含义如下:
- Device: 以sdX形式显示的设备名称
- tps: 每秒进程下发的IO读、写请求数量
- KB_read/s: 每秒从驱动器读入的数据量,单位为K。
- KB_wrtn/s: 每秒从驱动器写入的数据量,单位为K。
- KB_read: 读入数据总量,单位为K。
- KB_wrtn: 写入数据总量,单位为K。
- 输出含义:
- rrqm/s: 每秒对该设备的读请求被合并次数,文件系统会对读取同块(block)的请求进行合并
- wrqm/s: 每秒对该设备的写请求被合并次数
- r/s: 每秒完成的读次数
- w/s: 每秒完成的写次数
- rkB/s: 每秒读数据量(kB为单位)
- wkB/s: 每秒写数据量(kB为单位)
- avgrq-sz:平均每次IO操作的数据量(扇区数为单位)
- avgqu-sz: 平均等待处理的IO请求队列长度
- await: 平均每次IO请求等待时间(包括等待时间和处理时间,毫秒为单位)
- svctm: 平均每次IO请求的处理时间(毫秒为单位)
- %util: 采用周期内用于IO操作的时间比率,即IO队列非空的时间比率
- 重点关注参数:
1、iowait% 表示CPU等待IO时间占整个CPU周期的百分比,如果iowait值超过50%,或者明显大于%system、%user以及%idle,表示IO可能存在问题。
2、avgqu-sz 表示磁盘IO队列长度,即IO等待个数。
3、await 表示每次IO请求等待时间,包括等待时间和处理时间
4、svctm 表示每次IO请求处理的时间
5、%util 表示磁盘忙碌情况,一般该值超过80%表示该磁盘可能处于繁忙状态。
如果 %util 接近 100%,说明产生的I/O请求太多,I/O系统已经满负荷,该磁盘可能存在瓶颈。如果 svctm 比较接近 await,说明 I/O 几乎没有等待时间;如果 await 远大于 svctm,说明I/O 队列太长,io响应太慢,则需要进行必要优化。如果avgqu-sz比较大,也表示有当量io在等待。
6、dm-0、dm-1、dm-2的主设备号是253(是linux内核留给本地使用的设备号),次设备号分别是0、1、2,这类设备在/dev/mapper中
6. 监控实现
#!/bin/bash
#Description:
#Author:
#Created Time:
#磁盘io监控 明确知晓HD的使用情况
#监控指标 IO队列长度 IOPS 吞吐量
# iostat iozone
#iostat 队列长度 明确反馈IO是否忙
#iostat 由sysstat
#iostat 命令
io() {
device_num=`iostat -x |egrep "^sd[a-z]"|wc -l`
iostat -x 1 3|egrep "^sd[a-z]"|tail -n +$((device_num+1))|awk '{io_long[$1]+=$9}END{for (i in io_long)print io_long[i],i}'
}
#阈值如何判断 2-3
#while true
#do
io
# sleep 5
#done
六、mysql binlog日志备份
1. 脚本思路
mysql binlog日志备份脚本 备份到备份服务器
#1) 确定binlog的位置及备份时间间隔 每天
#当前要备份的binlog是谁
#刷新binlog日志,生成新的binlog用于存储备份节点后的数据
#2)打包binlog日志 以年-月-日_binlog.tar.gz格式
#3)生成校验码 md5sum
#4)将校验码和压缩包存入到文件夹 文件夹命名 年-月-日 再次打包
#5)使用scp拷贝到备份机器
#6)备份机器解压收到的目录压缩包 通过校验码 教研binlog压缩包是否完整
#完整 完成备份 -----发邮件给管理员,明确备份成功
#不完整 报错------发邮件给管理员,要求手动备份
2. 代码如下
#!/bin/bash
#Description:
#Author:
#Created Time:
#将mysql的binlog日志备份到备份服务器
#思考
#1) 确定binlog的位置及备份时间间隔 每天
#当前要备份的binlog是谁
#刷新binlog日志,生成新的binlog用于存储备份节点后的数据
#2)打包binlog日志 以年-月-日_binlog.tar.gz格式
#3)生成校验码 md5sum
#4)将校验码和压缩包存入到文件夹 文件夹命名 年-月-日 再次打包
#5)使用scp拷贝到备份机器
#6)备份机器解压收到的目录压缩包 通过校验码 教研binlog压缩包是否完整
#完整 完成备份 -----发邮件给管理员,明确备份成功
#不完整 报错------发邮件给管理员,要求手动备份
########
db_user='root'
db_password=''
log='/var/log/mysql_backup.log'
#
backup() {
#获得信息
binlog_dir='/var/lib/mysql'
current_binlog=`mysql -u $db_user -e "show master status"|egrep "binlog.[[:digit:]]*"|awk '{print $1}'`
date >> $log
#准备备份
#1 刷新binlog
mysql -u $db_user -e "flush logs"
#2 打包要备份的binlog
tar czf `date +%F`_binlog.tar.gz $binlog_dir/$current_binlog &>>$log
#3 生成校验码
md5sum `date +%F`_binlog.tar.gz > "`date +%F`_md5sum.txt"
#4 存入文件夹
[ ! -d `date +%F` ]&&mkdir `date +%F`
mv `date +%F`_binlog.tar.gz `date +%F`
mv `date +%F`_md5sum.txt `date +%F`
# 打包目录
tar czf `date +%F`.tar.gz `date +%F` &>>$log
#5 拷贝
#要求提前做证书信任
scp `date +%F`.tar.gz root@192.168.11.241:/opt/backup &>>$log
if [ $? -ne 0 ];then
echo "ERROR:scp `date +%F`.tar.gz fail" &>>$log
exit 1
fi
#6 校验
ssh root@192.168.11.241 "tar xf /opt/backup/`date +%F`.tar.gz -C /opt"
#ssh root@192.168.11.241 "cd /opt/backup`date +%F`"
ssh root@192.168.11.241 "cd /opt/`date +%F`;md5sum -c `date +%F`_md5sum.txt" &>>$log
if [ $? -eq 0 ];then
echo "success" &>>$log
ssh root@192.168.11.241 "rm -rf /opt/`date +%F`"
else
echo "fail" &>>$log
fi
}
backup
七、新建用户脚本
需求:新建 user01 - user20用户,要求密码是随机6位数 密码取值范围 a-zA-Z0-9,要求密码不能只是单一的数字或小写或大写字母
#!/bin/bash
#Description:
#Author:
#Created Time:
#创建user01-user20 随机六位数密码 a-zA-Z0-9
#1)创建user01-user20
#2)生成20组随机密码
#3)设置密码
#4)输出清单
adduser () {
pw_txt=`mktemp pw.XXXXX`
echo -e "account\t\tpassword" > user_add_result.txt
#1)创建用户
for i in `seq -s ' ' -w 1 20`
do
useradd user$i
done
#2)随机密码
cat /dev/urandom |strings -6|egrep "^[a-zA-Z0-9]{6}$"|head -n 20 > $pw_txt
#3)设置密码
for i in `seq -s ' ' -w 1 20`;do
pw=`head -n $i $pw_txt|tail -1`
echo $pw|passwd --stdin user$i &>/dev/null
echo -e "user$i\t\t$pw" >> user_add_result.txt
done
#4)输出
clear
echo "用户创建成功,密码文件是:user_add_result.txt"
cat user_add_result.txt
rm -rf $pw_txt
}
adduser