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};如果不加就会出现问题!

  1. 本地变量:用户私有变量,只有本用户可以使用,保存在家目录下的.bash_profile、.bashrc文件中
  2. 全局变量:所有用户都可以使用,保存在/etc/profile、/etc/bashrc文件,可使用 export 命令查看系统变量
  3. 用户自定义变量:用户自定义,比如脚本中的变量
  4. 位置变量:可以放在任何位置的变量,$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
  1. 预定义变量:$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 高级应用

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
posted @ 2023-07-21 15:36  ArMinLi  阅读(18)  评论(0编辑  收藏  举报