shell脚本:介绍、语法、运算、流程控制、对文件/输出流处理、案例

Shell介绍

shell是一个程序、采用c语言编写,是用户和linux内核沟通的桥梁。它既是一种命令语言,又是一种解释性的编程语言。

内核是管理电脑的所有硬件,同时内核还要负责驱动硬件干活,就比如说我们需要打印一张照片,内核就会驱动打印机去干活。这是内核的两个主要的功能。用户下指令给内核,内核再让相应的硬件设备去干活,但是用户与内核的语言不通,内核只认识机器语言,而用户是高级语言,shell的作用就体现在这里了,它就是解释命令的。

shell功能:

  1. 命令行解释功能
  2. 启动程序
  3. 输入输出重定向
  4. 管道连接
  5. 文件名置换
  6. 变量维护
  7. 环境控制
  8. shell编程


# Shell语法 hell脚本就是将完成一个任务的所有的命令按照执行的先后顺序,自上而下写入到一个文本文件中,然后给予执行权限。

这就是shell脚本的本质,现在做一个案例,编写一个shell脚本 用来安装nginx。

我先创建一个shell的文件夹,接下来所有有关shell的练习都在这里面。/home/hs/shell

  1. 首先在Nginx的官网中复制一个安装包的下载链接
    在这里插入图片描述

  2. 在练习shell的目录下创建一个文件vim nginx_install.sh

#!/usr/bin/bash
# 安装nginx步骤:
# 1. 要安装nginx,首先需要安装依赖
yum -y install wget gcc pcre-devel zlib-devel
# 2. 下载nginx
wget https://nginx.org/download/nginx-1.20.1.tar.gz
# 3. 解压nginx压缩包
tar xf nginx-1.20.1.tar.gz
# 4. 进入解压后的目录
cd nginx-1.20.1
# 5. 执行configure文件
./configure --prefix=/use/local/nginx
# 最后执行make命令
make
make install
  1. 给文件执行权限

    [root@VM-8-7-centos shell_01]# chmod 700 nginx_install.sh 
    
  2. 然后在执行刚刚创建的shell脚本

    [root@VM-8-7-centos shell_01]# pwd
    /home/hs/shell/shell_01
    [root@VM-8-7-centos shell_01]# ls
    aaa_explain.txt  nginx_install.sh
    [root@VM-8-7-centos shell_01]# ./nginx_install.sh 
    
  3. 安装完成之后然后在启动

    # nginx的常用命令
    cd /usr/local/nginx/sbin/
    ./nginx  启动
    ./nginx -s stop  停止
    ./nginx -s quit  安全退出
    ./nginx -s reload  重新加载配置文件
    ps aux|grep nginx  查看nginx进程
    
  4. 然后在通过浏览器访问,就能出现如下界面
    在这里插入图片描述


shell脚本的命令

  1. 文件名尽量见名知意,不要瞎起

  2. 在shell脚本的第一行尽量使用#! 写一段定义脚本的执行环境。#! 在shell中是一个特例,这不是注释 。 这是定义该shell脚本在哪个环境下执行 写在第一行

  3. # 表示注释 在以后的shell脚本执行时,请不要再里面写中文 即使是注释也不要写中文

  4. 在定义脚本的执行环境下还需要加一下的提示信息

    #!/usr/bin/bash
    # Author:Hu Shang
    # Created Time: 2021/8/24 19:27
    # Script Description: nginx install script
    

脚本的组成:

  • 解释环境
  • 注释说明
  • 执行代码

运行shell脚本

运行shell脚本有两种方式:

  • 给执行权限
  • 使用解释器直接运行,这种方式就不需要给执行权限了

比如我现在创建一个入门文件,输入hello world!

vim shell_helloWorld.sh

#!/usr/bin/bash
# Author:Hu Shang
# created Time: 2021/8/24 19:49
# script Description: shell hello world!!

echo "hello world!"

我现在可以使用chmod 700 shell_helloWorlf.sh 命令给改文件设置执行权限 然后直接./shell_helloWorld.sh

另一种方式就是:

给该文件不设置执行权限chmod 644 shell_helloWorlf.sh,然后使用

# 先没有设置执行权限,然后利用bash命令 使用解释器也可以运行  然后就出现了下面的输入语句
[root@VM-8-7-centos shell_01]# chmod 644 shell_helloworld.sh 
[root@VM-8-7-centos shell_01]# bash shell_helloworld.sh    
hello world!

# 处理bash 我还可以使用sh 解释器来运行shell脚本, 下面六种都支持 但是建议脚本中第一行指定的什么执行环境就使用什么
[root@VM-8-7-centos shell_01]# sh shell_helloworld.sh 
hello world!
[root@VM-8-7-centos shell_01]# cat /etc/shells 
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
/bin/tcsh
/bin/csh

bash -x 文件名 以debug模式运行shell脚本


特殊符号

  • ~ 进入到家目录 比如 cd ~

  • ! 执行历史命令 !! 这是执行上一条命令

  • $ 变量中取内容符 比如我想知到当前用户是谁echo $USER

  • + - * / % 对应数学中的加减乘除取余

  • & 后台执行

  • * 通配符 shell中匹配所有字符

  • ? 通配符 shell中匹配除回车之外的单个字符

  • ; 如果在一行中需要执行多条命令就需要使用分号

  • | 管道符 上一个命令的输出作为下一个命令的输入 例如 cat filename|grep "abc"

  • \ 转义字符

  • `` 这两个反引号 作用是在命令中执行命令

    [root@VM-8-7-centos ~]# echo -n “date is:”;date +%F
    date is:2021-08-24
    [root@VM-8-7-centos ~]# echo "date is: `date +%F`"
    date is: 2021-08-24
    
  • ' ' 单引号 脚本中出现的字段可以用单引号引起来 单引号不解释变量

  • " " 双引号 脚本中出现的字段可以用双引号引起来

    [root@VM-8-7-centos ~]# echo 'date is: `date +%F`'
    date is: `date +%F`
    [root@VM-8-7-centos ~]# echo "date is: `date +%F`"
    date is: 2021-08-24
    # 这就是区别  如果用单引号 那就是原样输出  不会去解释变量
    


管道符

管道符 | 在shell中是使用的很多的,管道就是上一个命令的输出作为下一个命令的输入。

比如我要查看这个文件的内容,命令如下:

[root@VM-8-7-centos shell_01]# cat shell_helloworld.sh 
#!/usr/bin/bash
# Author:Hu Shang
# created Time: 2021/8/24 19:49
# script Description: shell hello world!!

echo "hello world!"

但是如果我不想输出改文件了,我想把这个通过管道符 输入给grep 命令 让改命令检索某个字符串,

他就会把这段文字中包含某个字符串的这一行内容找出来

[root@VM-8-7-centos shell_01]# cat shell_helloworld.sh | grep "hello world"
# script Description: shell hello world!!
echo "hello world!"

重定向

首先是重定向输入

# 将haha字符串输入到test.txt文件中
[root@VM-8-7-centos shell_01]# echo haha > ./test.txt
# 但是这个命令是一次性输入,这条命令会先将文件内容清空,然后进行输入操作。如果我在使用它输入其他字符串,就会把上一次输入的内容替换掉
# 如果我不想清空原来的内容 想进行追加的操作:就需要使用 >>
[root@VM-8-7-centos shell_01]# echo haha > test.txt 
[root@VM-8-7-centos shell_01]# cat test.txt
haha
[root@VM-8-7-centos shell_01]# echo aaa >> test.txt 
[root@VM-8-7-centos shell_01]# echo bbb >> test.txt 
[root@VM-8-7-centos shell_01]# cat test.txt 
haha
aaa
bbb

接下来是重定向输出

就比如我使用wc命令来统计一个文件的 行数 单词数 字节数

[root@VM-8-7-centos shell_01]# wc < ./test.txt 
 3  3 13

这行命令是把这个文件的内容从内容中输出给 wc 它再进行统计

# 这个统计的是数据流
[root@VM-8-7-centos shell_01]# wc < ./test.txt 
 3  3 13
 # 这里统计的是文本
[root@VM-8-7-centos shell_01]# wc ./test.txt 
 3  3 13 ./test.txt

追加输出
我要写一个有关与磁盘分区相关的脚本

[root@VM-8-7-centos shell_01]# vim disk_partition.sh

脚本内容为:

#!/usr/bin/bash
# Author: hu shang
# Created Time: 2021/8/25 20:57
# Script Description: harddisk partition script

fdisk /dev/sdb <<EOF
n
p
3

+534M
w
EOF

重定向追加输出,用 <<

  • 然后使用EOF或者是END充当左右括号 将要输入的内容括起来
  • 将我们要交互的内容 顶格写在EOF


格式化输入与输出

格式化输出

echo 该命令负责输出,将内容输出到默认显示设备

语法: echo [-n -e] 字符串

echo命令将字符串输出之后会直接换行

命令选项:

  • -n 不要再最后换行
  • -e 让字符串支持转义字符,如果不加该属性,字符串中即使有转义字符也只会把它当成普通字符串输出

转义字符:

\a 发出报警

\b 删除前一个字符

\c 最后不加换行

\f \v 换行,但光标仍停留在原来的位置

\n 换行,且光标移至行首

\r 光标移至当前行首,不会换行

\t 制表位

\nnn 插入nnn(八进制)所代表的ASCII字符

一个输出倒计时的案例

#!/usr/bin/bash
# Author: hushang
# Created Time: 2021-08-26 19:00
# Script Description: number down

for time in `seq 9 -1 0`;do
        echo -n -e "$time"
        sleep 1
        echo -n -e "\b"
done

echo 

颜色代码

让输出的字符串加上一些背景颜色 字体颜色

echo -e "\033[背景色;字体颜色m 字符串 \033[字体效果m"

# 字符串前后可以没有空格,如果有的话,输出也是同样有空格

[root@VM-8-7-centos shell_02]# echo -e "\033[44;31m 你好呀 \033[0m"
 你好呀 

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
我在最后给输出的字符串加上了下划线的选项,导致了下面的命令都有改样式了,我只需要将最后一个样式 变为0m 即可

在这里插入图片描述


## 格式化输入 `read` 命令 默认接受键盘的输入,回车符代表输入结束

当shell脚本执行到read命令时就不会执行了,会停下来

命令选项:

  • -p 打印信息

  • -t 限定时间

    read -t5 temp 有输入就存放在temp变量中,如果五秒中没有输入那就不会 一直等待了

  • -s 不回显, 就比如输入密码时 别人就看不到你输入的内容了

  • -n 输入字符个数

    read -n5 temp 这就是只能输入五位数,只能识别输入的五位数,当用户输入五位数后程序就自动往下运行了

案例 写一个简单的登录

vim login.sh

#!/usr/bin/bash
# Author: hu shang
# Created Time: 2021-08-26 20:28
# Script Discrption: test input,login

clear
echo -n -e "login: "
read
echo -n -e "password: "
read

然后执行该脚本

[root@VM-8-7-centos shell_02]# bash login.sh 
login: 123
password: 123
[root@VM-8-7-centos shell_02]# 

但是现在的问题的,我们输入的内容的确的输入了,并且放在了内存中,可是并没有定位,根本不知道存放的位置,也就使用不了,所以需要使用一个逻辑变量名

#!/usr/bin/bash
# Author: hu shang
# Created Time: 2021-08-26 20:28
# Script Discrption: test input,login

clear
echo -n -e "login: "
read acc
echo -n -e "password: "
read pass

echo "username: $acc   password: $pass"   # 这里就能将我上面的输入拿来用了。

这其实就是一个变量,变量就是在内存中找一块位置,做一个定位,定好位后就将我们输入的内容存到该定位中

我每次都需要使用两个语句: echo 和 read 这两个命令可以结合为一条命令 也就是上方的-p 命令参数

#!/usr/bin/bash
# Author: hu shang
# Created Time: 2021-08-26 20:28
# Script Discrption: test input,login

clear
# echo -n -e "login: "
# read acc
read -p "login: " acc
echo -n -e "password: "
read pass

echo "username: $acc   password: $pass"   # 这里就能将我上面的输入拿来用了。


运算

shell数学运算

expr 这个命令是用来做数学运算的

# 该命令有很严格是空格要求
[root@VM-8-7-centos shell_01]# expr 1+1
1+1
# 参与运算的数与运算符之间都需要空格
[root@VM-8-7-centos shell_01]# expr 1 + 1
2
# 参与运算的数必须是整数
[root@VM-8-7-centos shell_01]# expr 1 + 1.1
expr: non-integer argument
# 乘法必须转义
[root@VM-8-7-centos shell_01]# expr 1 * 2
expr: syntax error
[root@VM-8-7-centos shell_01]# expr 1 \* 2
2
[root@VM-8-7-centos shell_01]# expr 10 / 2
5
[root@VM-8-7-centos shell_01]# expr 10 % 3
1

比如,我要判断一个数是否为整数

# 可以先让要判断的数 +1
[root@VM-8-7-centos shell_01]# expr 8 + 1
9
# $? 是判断上一条命令是否执行成功,如果是0就表示执行成功   如果是
[root@VM-8-7-centos shell_01]# echo $?
0
# 执行失败的情况
[root@VM-8-7-centos shell_01]# expr 7 + 7.1
expr: non-integer argument
[root@VM-8-7-centos shell_01]# echo $?
2
# 还可以将上一条命令的输出 不进行输出  让它输出到回收站中去。然后判断上一条命令是否执行成功
[root@VM-8-7-centos shell_01]# expr 7 + 7.1 &>/dev/null ; echo $?
2

数学运算还有另一个命令let ,它必须将结果赋值到一个变量中去,然后进行其他操作

[root@VM-8-7-centos shell_01]# let sum=1+2+3
[root@VM-8-7-centos shell_01]# echo $sum
6

let 命令也是整数的运算

但是在shell中难免会出现小数的运算,就可以使用bc 计算器

[root@VM-8-7-centos shell_01]# bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
1+1
2

13-11
2

4/2
2

3*3
9

# 但是bc计算器默认也不支持小数运算,需要使用下面的命令指定一个小数位,然后就可以执行小数运算了
scale=2
10/3
3.33

bc 计算器是一个交互界面,shell脚本中不能交互,这就用到了管道

[root@VM-8-7-centos shell_01]# echo "scale=2;10/4" | bc
2.50

[root@VM-8-7-centos shell_01]# echo "百分数:`echo "scale=2;10/2"|bc`%"
百分数:5.00%

在shell下 $(( )) 就可以做运算

[root@VM-8-7-centos shell_01]# $(( 10+20))
-bash: 30: command not found
[root@VM-8-7-centos shell_01]# echo $(( 10+50))
60
[root@VM-8-7-centos shell_01]# echo $((10/3))
3
[root@VM-8-7-centos shell_01]# echo $((10%3))
1



变量

在编程中,我们总有一些数据需要临时存放在内存中,以待后续使用时快速读出。内存在系统启动的时候按照1B一个单位划分为若干块,然后统一编号(16进制编号),并对内存的使用情况做记录,保存在内存跟踪表中。

假如我将1B的字符存入内存中,如何读取出来?就是通过变量的

变量:是编程中最常用的一种临时在内存中存取数据的一种方式

就比如我们 0X3 这个位置是内存中的第三块,计算机认识,可以我们不知道,那么我们就可以为这块区域起一个逻辑名字,然后在往这块区域中存值。到时候取值也可以使用0X3 这个物理地址 也可以使用我们定义的逻辑名字

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h7MlVWYo-1631417107601)(E:\Java笔记\picture\image-20210827132419037.png)]

所以变量就是内存中一块地址的一个逻辑名字。

当我们从脚本中定义变量存值,可以从以下方面看到变化:

  1. 内存占用:如果存的是一个字符则占用一个字节,如果存的是字符串这占用长度+1个字节。因为最后有一个'\0'代表结束标识

  2. 变量名与空间的对应关系:计算机会将对应的内存空间和变量名绑定在一起,此时代表这段空间已经被程序占用,其他程序不可用,然后将变量名对应的值存入对应的内存空间。


变量的分类

  • 本地变量:用户私有变量,只有本用户可以使用,保存在家目录下的.bash_profile 、 .bashrc 文件中。用户登录成功后才将命令加载进内存
  • 全局变量:所有用户都可以使用,保存在/etc/profile/etc/bashrc 文件中。在用户登录之前就加载进内存中
  • 用户自定义变量: 用户自定义,比如脚本中的变量。

建议变量的命名采用大写 和命令区分。

#!/usr/bin/bash
# Author: hu shang
# Created Time: 2021-08-27 21:14
# Script Discription: test user custom variable

# 这里定义几个变量
NAME='Hu Shang'
AGE=21
SCORE=100

# 这里读取
echo "name=$NAME , age=$AGE"

定义变量

格式:变量名=值

注意:

  • shell编程中变量名与等号之间不能有空格,
  • 变量的命名参照java命名规范,不能将变量名定义为bash中的命令(保留关键字)
  • 字符串要用单引号或双引号 引起来。
  • shell是区分大小写的,建议将变量命名全改为大写,这样可以和命令区分开

读取变量内容

可以通过 $ 来读取变量中的内容 格式:$变量名


取消变量

unset 变量名

当我们定义了一个变量,然后不想使用了,可以将电脑关机 存在内存中的变量就没了,也可以使用unset 命令来实现

[root@VM-8-7-centos shell_02]# NAME='胡 尚'
[root@VM-8-7-centos shell_02]# echo $NAME
胡 尚
[root@VM-8-7-centos shell_02]# unset NAME
[root@VM-8-7-centos shell_02]# echo $NAME

[root@VM-8-7-centos shell_02]# 

使用这个命令可以取消用户自定义变量和本地变量、全局变量。比如我现在定义一个全局变量:

  1. /etc/profile目录下添加一个变量

    # 首先在/etc/profile 文件下添加变量
    vim /etc/profile
    
    # 第二个 添加变量
    # 这样定义的一个变量 即使是在/etc/profile 文件中定义的变量,但其实还是一个本地变量,其他用户还是使用不了
    NAME='胡 尚'
    # 需要加上export 关键字
    export NAME='胡 尚'
    
    # 修改了这个文件 还需要使用source命令让它生效
    source /etc/profile
    

定义永久变量

写在命令行就是临时变量,关机重启就没了 如果想定义永久变量就定义在上面提到的文件中


特殊变量

  • $0 代表当前脚本的名字
  • $1 $2 … 代表传参
  • $* 代表所有的参数,其间隔为IFS内定参数的第一个字元
  • @ 与 @ 与 @*相同 不同之处在于不参照IFS
  • $# 参数的数量
  • $ 执行上一条指令的返回值
  • $$ 脚本执行进程号
  • $_ 显示出最后一个执行命令

数组

变量和数组的区别就是,变量一次只能存一个值,数组一次可以存多个值。


基本数组

可以让用户一次性赋予多个值,使用时通过索引来调用值

定义格式:数组名=(元素1 元素2 元素3 ...)

数组的读取:${数组名[索引]} 索引从0开始

数组的赋值:

  • 一次性赋值多个 数组名=(元素1 元素2 元素3 ...)
  • 一次性赋值一个 数组名[索引]= 元素

访问数组元素:

  • echo ${数组名[0]} 访问数组中第一个元素
  • echo ${数组名[@]} 访问数组中全部元素
  • echo ${#数组名[0]} 统计数组元素个数
  • echo ${数组名[@]:2} 从索引为2的位置开始打印数组中的所有元素
  • echo ${数组名[@]:2:4} 从索引为2的位置开始打印 共打印4个元素
  • echo ${!数组名[@]} 获取数组元素的索引
#!/usr/bin/bash
# Author: hu shang
# Created Time: 2021-08-28 10:56
# Script Discrption: array

ARRAY1=('a' 'b' 'c' 'd')
ARRAY1[4]='e'  # 等号前后都不要有空格
ARRAY1[5]='f'

echo "根据数据索引来获取值:"
echo ${ARRAY1[0]}
echo "获取全部值:"
echo ${ARRAY1[@]}
echo "获取数组长度:"
echo ${#ARRAY1[@]}
echo "从索引为2的位置开始打印:"
echo ${ARRAY1[@]:2}
echo "从索引为2的位置开始打印,共打印3个"
echo ${ARRAY1[@]:2:3}
echo "输出数组的索引:"
echo ${!ARRAY1[@]}

输出结果为:

[root@VM-8-7-centos shell_02]# bash array.sh 
根据数据索引来获取值:
a
获取全部值:
a b c d e f
获取数组长度:
6
从索引为2的位置开始打印:
c d e f
从索引为2的位置开始打印,共打印3个
c d e
输出数组的索引:
0 1 2 3 4 5

索引也可以自定义,但是就需要使用到关联数组了


关联数组

关联数组允许用户自定义索引,这样使用起来更方便 高效。在创建关联数组时需要先声明,如果不声明那么创建的就是一个基本数组

#!/usr/bin/bash
# Author: hu shang
# Created Time: 2021-08-28 11:25
# Script Discription: associate array

# 声明一个关联数组
declare -A ASS_ARRAY1
# 然后一次性赋一个值
ASS_ARRAY1[name]='hushang'
ASS_ARRAY1[age]=21
echo ${ASS_ARRAY1[name]}


# 声明第二个关联数组 一次性赋多个值
declare -A ASS_ARRAY2
ASS_ARRAY2=([name]='aaaaa' [age]=22)
echo "第二个关联数组的name属性值是:${ASS_ARRAY2[name]}"

五大运算


数学比较运算

这里比较是的整形。

  • -eq 等于
  • -gt 大于
  • -lt 小于
  • -ge 大于等于
  • -le 小于等于
  • ne 不等于
# test命令可以用来做判断 但是不会输出结果   可以使用echo $? 输出上一条命令的结果, 如果为0就表示成功 非0表示失败
[root@VM-8-7-centos shell_02]# test 1 -eq 1; echo $?
0
[root@VM-8-7-centos shell_02]# test 1 -gt 1; echo $?
1
[root@VM-8-7-centos shell_02]# test 1 -ge 1; echo $?
0
[root@VM-8-7-centos shell_02]# test 1 -lt 1; echo $?
1
[root@VM-8-7-centos shell_02]# test 1 -le 1; echo $?
0
[root@VM-8-7-centos shell_02]# test 1 -ne 1; echo $?
1
[root@VM-8-7-centos shell_02]# 

如果某种情况下需要进行小数比较,可以先转换为整数 然后在比较,就比如1.5 和 2 进行比较,都*10 然后比较就可以了

[root@VM-8-7-centos shell_02]# echo "1.5*10"|bc
15.0

# cut -d 数字 分割命令      以 . 进行分割     -f1 是表示取分割都的第一个
[root@VM-8-7-centos shell_02]# echo "1.5*10"|bc|cut -d '.' -f1
15

[root@VM-8-7-centos shell_02]# test `echo "1.5*10"|bc|cut -d '.' -f1` -lt $((2*10));echo $?
0

字符串比较运算

首先字符串别忘了用双引号引起来,

  • == 等于
  • != 不等于
  • -n 检查字符串长度是否大于0
  • -z 检查字符串长度是否为0
[root@zutuanxue ~]# test 'root' == 'root';echo $?
0
[root@zutuanxue ~]# test 'root' != 'root1';echo $?
0
[root@zutuanxue ~]# test -n "$name";echo $?
1
[root@zutuanxue ~]# test -z "$name";echo $?
0

文件比较与检查

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

以上只列出部分命令选项,详细的可以通过:man test获得。

逻辑运算

  • && 逻辑与
  • || 逻辑或
  • 逻辑非

赋值运算

  • = 赋值运算符, 尽量在shell中前后就不要加空格了。


循环与流程控制

if


语法一:单if语句

满足条件后执行一些命令,这里的条件就是上面的五大运算 一般也就是 数字 字符串 文件 这三个的比较判断

if 	[ condition ]   # 两个中括号中需要有空格  
	then
		commands
fi

写一个脚本,当/tmp/abc这个目录不存在时就创建它

#!/usr/bin/bash
# Author: hu shang
# Created Time: 2021-08-28 16:14
# Script Description: 测试if 如果/tmp/abc 这个目录不存在就创建一个

if [ ! -d /tmp/abc ]
        then
                mkdir -v /tmp/abc
                echo "create /tmp/abc success"
fi

if-then-else语句

满足条件后执行一些命令,如果不满足就执行另一些

if 	[ condition ]   # 两个中括号中需要有空格  
	then
		commands1
	else
		commands2
fi

还是上面创建目录的案例

#!/usr/bin/bash
# Author: hu shang
# Created Time: 2021-08-28 16:14
# Script Description: 测试if 如果/tmp/abc 这个目录不存在就创建一个 如果存在就提示

if [ ! -d /tmp/abc ]
        then
                mkdir -v /tmp/abc
                echo "create /tmp/abc success"
else
                echo "creat /tmp/abc fail"
                echo "the dir already exist" 
fi

if-then-elif语句

if [ conditon ]
	then
		commands1
		
elif [ condition ]
	then
		commands2
		
elif [ condition ]
	then
		commands3
		
……
else
		commandsN
fi

案例如下

#!/usr/bin/bash
# Author: hu shang
# Created Time: 2021-08-28 16:51
# Script Description: 测试 if then elif 语句   输入两个数 然后比较大小

if [ $1 -eq $2 ]
        then
                echo "$1 == $2"
elif [ $1 -gt $2 ]
        then
                echo "$1 > $2"
else
                echo "$1 < $2"
fi

这里在执行这个脚本时,还可以在后面加上两个值 表示脚本中的$1 与 $2 两个变量

[root@VM-8-7-centos shell_02]# bash test_if_compare.sh 1 1
1 == 1
[root@VM-8-7-centos shell_02]# bash test_if_compare.sh 1 2
1 < 2
[root@VM-8-7-centos shell_02]# bash test_if_compare.sh 1 0
1 > 0

其他用法

条件符号使用双小圆括号,可以在条件中植入数学表达式。需要注意的就是 在双小圆括号中 比较是使用的我们传统的符号 而不是 -gt 这种

# if的另一用法 用两个小括号 可以用来做整数的判断
if (( 100+20 > 150 ));then
        echo "yes"
else
        echo "no"
fi

使用双方括号,可以在条件中使用通配符 可以做字符串匹配

# if 的另一种用法 使用两个方括号  可以用通配符
# 使用循环 将 rx开头的字符串打印出来
for TEMP_VAR in aaa abc dede rxaa rxbb rxcc
        do
                if [[ "$TEMP_VAR" == rx* ]];then
                        echo "$TEMP_VAR"
                fi
done

程序的判断越多 越智能。


for循环


for语法一

for 变量名 in value1 value2 ...
	do
		命令段
done

这个意思是,将in后面的值依次赋值给前面的临时变量,然后执行一次命令段,再赋值下一个value,在执行一次

#!/usr/bin/bash
# Author: hu shang
# Created Time: 2021-08-28 18:09
# Script Description: for循环的练习 

# 输出 1 - 9  之间的数
for var  in `seq 1 9`
        do
                echo "$var"
done

seq 这个命令 就是一个数数的命令

[root@VM-8-7-centos shell_02]# seq 1 5
1
2
3
4
5
[root@VM-8-7-centos shell_02]# seq 5 -1 1
5
4
3
2
1

语法二

for ((变量;条件;自增自减))
	do
		代码段
done


# 输出1-9 的脚本如下
for ((i=1;i<10;i++))
        do
                echo "$i"
done

# 当有多个字段时,也和java没什么区别
for ((i=1,j=9;i<10,j>0;i++,j--))
        do
                echo "$i --- $j"
done

循环控制语句

  • sleep N 休眠N秒钟
  • continue 跳出本轮循环
  • break 终止循环
#!/usr/bin/bash
# Author: hushang
# Created Time: 2021-8-30 12:46
# Script Description: 循环控制 sleep    continue     break

# 每隔一秒输出1-9   但是跳过5
for ((i=1;;i++))
        do
                # 是否等于5
                if [ $i -eq 5 ]
                        then
                                continue
                fi
                # 大于9就跳出循环
                if [ $i -gt 9 ]
                        then
                                break
                fi
        echo $i
        sleep 1
done

break 后面还可以跟一个数字

for ((;;))
	do
		echo "aaa"
		
		for ((;;))
			do
				echo "bb"
				break 2   # 这里如果写1  就表示跳过离break最近的这个内循环    如果写2 这里就表示跳出外循环了
		done
done

while循环

如果明确知道了循环的次数就推荐使用for,如果不知道循环次数还是推荐使用while循环。

语法:

while [ condition ] 
	do
		循环体
done

# 可以在多个条件之间使用逻辑运算符
while [ condition ] && [ condition ]
	do
		循环体
done

while还有一些嵌套使用,还是比较简单,嵌套if while for


until循环

until循环和while循环语法上一样,区别是,while是当条件为true时继续循环,而until刚好相反,条件为false时才循环

until [ condition ] 
	do
		循环体
done

case多分支语句

语法:

case 变量 in
条件1) 
	代码段
;;
条件2)
	代码段
;;
......
# 如果都不满足 则执行默认的代码段
*)
	代码段
;;
esac

# 每个代码块执行完使用;;结尾,  case开始 esac倒过来写表示改语句结束

小案例

#!/usr/bin/bash
# Author: hushang
# Created Time: 2021-08-31 13:04
# Script Description: case多分支语句的练习  根据用户输入的值来输出对应的字符串

for ((;;))
do
        read -p "number: " N
        case $N in
        1)
                echo "aaaaa"

        ;;
        2)
                echo "bbbbb"
        ;;
        3)
                echo "ccccc"
        ;;
        4)
                break
        ;;
        *)
                echo "输入有误,请输入{1|2|3|4}  输入4退出"
        ;;
        esac
done
# 如果当有多个条件中的任意一个满足就执行某个case分支的代码段的话 可以使用
case 变量 in
条件1|条件2) 
	代码段
;;
*)
	代码块
;;
esac


shell其他知识

shell函数

函数的优点就是代码模块化。函数也就是方法。

语法:

# 语法一
函数名 () {
	代码块
	return N
}

# 语法二
function 函数名 {
	代码块
	return N
}

上面的仅仅是定义函数,如果想调用的话 直接写函数名。比如

start () {
	echo "start .......     [ok]"
}

# 启动函数
start

/etc/init.d/functions 目录下有很多的函数


正则表达式

它是文本模式匹配,作用就是用它提供的特殊字符生成一个公式,用这个公式从海量的数据中筛选出我需要的数据。

shell也支撑正则表达式,但并不是所有命令都支撑,常见的支撑的命令有:grep sed awk

特殊字符

定位符说明
^锚定一个开头字符
&锚定一个结尾字符

如果两个符号一起使用就是精确匹配,如果只是使用其中一个是模糊匹配。

# 假如现在有一个文件 file.txt  中存放了很多字符串
# 查询以a字符串开头     grep命令默认是不支持正则表达式的,需要加上-e属性才能支撑
[root@VM-8-7-centos shell_02]# grep -e "^a" file.txt

# grep -e    等于  egrep
# 查询以c结尾的字符串
[root@VM-8-7-centos shell_02]# egrep "c&" file.txt

# 查询以a开头c结尾的字符串 下面的写法是精确匹配  最后的结果只能查询出来ac字符串
[root@VM-8-7-centos shell_02]# egrep "^ac&" file.txt
匹配符说明
.匹配一个除回车外的任意字符
()字符串分组 , 使用场景比如以a或b字符开头
[]定义字符串,匹配括号中的一个字符
[^]正好和上面相反,表示除了括号中的字符不匹配,其他的字符都匹配
\转义
|
# 匹配以a开头c结尾的字符串,这两个字符之间可以有一个任意字符  但仅仅只能有一个
[root@VM-8-7-centos shell_02]# egrep "^a.c&" file.txt

# 我想以a或者b开头  c结尾
[root@VM-8-7-centos shell_02]# egrep "^(a|b)c&" file.txt

# 我想以a开头c结尾  中间有任意一个数字
[root@VM-8-7-centos shell_02]# egrep "^a[0-9]c&" file.txt

# 我想以a开头c结尾  中间有任意一个数字
[root@VM-8-7-centos shell_02]# egrep "^a[0-9]c&" file.txt

# 假如我就是想查询 "a*b" 字符串  但是*在正则中又有特殊含义  所以就需要转义了
[root@VM-8-7-centos shell_02]# egrep "^a\*c&" file.txt
限定符说明
*某个字符之后加*表示改字符可以出现[o,+∞]次
某个字符出现[0,1]次
+某个字符出现[1,+∞]次
{n,m}某个字符出现[n,m]次
{m}正好出现m次
# 我想查询以a开头c结尾   中间有任意多个数字
[root@VM-8-7-centos shell_02]# egrep "^a[0-9]*c&"

posix特殊字符
在这里插入图片描述
[:alnum:] 这个是一个字符串,使用是需要 [[]] ,最外面的方括号表示一个字符,里面的就是一个posix特殊字符。

# 我想查询以a开头c结尾   中间可以有一个任意字母字符
[root@VM-8-7-centos shell_02]# egrep "^a[[:alnum:]]*c&"

对文件的操作

对文件进行增删改的操作,使用vim的确也可以实现,但vim命令是交互式的,shell脚本不支持交互式去调用文件,这就需要使用到sed命令,它可以直接对文本进行操作而不需要交互

sed命令,是linux中提供的一个行编辑器命令,使用者只能在命令行输入编辑命令、指定文件名,然后在屏幕上查看输出,它和文本编辑器的区别是文本编辑器是针对文本,而行编辑器针对的是文件中的某一行,它一次处理文件中的某一行

如果一个文件几百MB,直接用vim进行打开会读取全都文件的内容,比较暂用文件内存,所以对于大文件可以使用行编辑器。

sed数据处理原理

首先读取文件中的一行数据到内存中,然后在内存中进行处理,然后输出到电脑屏幕。所以它默认不会修改源文件,修改的只是读取到内存中的数据。如果想修改文件中的内容就需要加一个命令选项

语法:

sed [options] '{command}[flags]' [文件名]      
# 其中{} 是必填项

各个位置能够填写的参数是

#options选项
-e script 将脚本中指定的命令添加到处理输入时执行的命令中  多条件,一行中要有多个操作
-f script 将文件中指定的命令添加到处理输入时执行的命令中
-n        抑制自动输出
-i        编辑文件内容
-i.bak    修改时同时创建.bak备份文件。
-r        使用扩展的正则表达式
!         取反 (跟在模式条件后与shell有所区别)

#command   对文件干什么
sed常用内部命令
a   在匹配后面添加
i   在匹配前面添加
d   删除
s   查找替换  字符串
c   更改
y   转换   N D P 
p   打印



#flags
数字             表示新文本替换的模式
g:             表示用新文本替换现有文本的全部实例
p:             表示打印原始的内容
w filename:     将替换的结果写入文件

首先创建一个文本文件,并输入一些内容

[root@VM-8-7-centos shell_04]# vim data.txt

1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

开始set命令

# 在每一行后面添加hello字符串
[root@VM-8-7-centos shell_04]# sed 'ahello' data.txt 
1 the quick brown fox jumps over the lazy dog.
hello
2 the quick brown fox jumps over the lazy dog.
hello
3 the quick brown fox jumps over the lazy dog.
hello
4 the quick brown fox jumps over the lazy dog.
hello
5 the quick brown fox jumps over the lazy dog.
hello

# 第一个字母就表示命令 为了好区分开可以在命令和要插入的数据之间加一个 \
[root@VM-8-7-centos shell_04]# sed 'a\hello' data.txt 
1 the quick brown fox jumps over the lazy dog.
hello
2 the quick brown fox jumps over the lazy dog.
hello
3 the quick brown fox jumps over the lazy dog.
hello
4 the quick brown fox jumps over the lazy dog.
hello
5 the quick brown fox jumps over the lazy dog.
hello

# 还可以在指定的一行后面添加数据  比如在第三行添加数据  只是在命令前面加一行即可
[root@VM-8-7-centos shell_04]# sed '3a\hello' data.txt 
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
hello
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

# 还可以在一个范围中添加数据  就比如在2-4行添加数据
[root@VM-8-7-centos shell_04]# sed '2,4a\hello' data.txt 
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
hello
3 the quick brown fox jumps over the lazy dog.
hello
4 the quick brown fox jumps over the lazy dog.
hello
5 the quick brown fox jumps over the lazy dog.

# 上面的几种都不太适用于生产的环境,因为到时候不可能一行一行的去数
# 有一种匹配模式  可以搜索 特定的一些行进行新增 sed '/特定字符串/命令\要新增的字符串' 文件名
[root@VM-8-7-centos shell_04]# sed '/3 the quick/a\hello' data.txt 
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
hello
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

# a 命令是在一行的后面进行追加,i是在一行的前面进行新增
[root@VM-8-7-centos shell_04]# sed 'i\hello' data.txt 
hello
1 the quick brown fox jumps over the lazy dog.
hello
2 the quick brown fox jumps over the lazy dog.
hello
3 the quick brown fox jumps over the lazy dog.
hello
4 the quick brown fox jumps over the lazy dog.
hello
5 the quick brown fox jumps over the lazy dog.
# 还有其他几个
[root@VM-8-7-centos shell_04]# sed '3i\hello' data.txt       # 在第三行插入字符串
[root@VM-8-7-centos shell_04]# sed '2,4\hello' data.txt 	# 在第2-4行插入字符串
[root@VM-8-7-centos shell_04]# sed '/3 the qu/\hello' data.txt 		# 在这一个特定字符串的这一行插入字符串

# 删除第三行
[root@VM-8-7-centos shell_04]# sed '3d' data.txt 
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

sed命令默认不支持正则表达式,可以加一个-r的名称选项,比如我想删除一个文件中所有# 开头的注释以及 空行

[root@VM-8-7-centos shell_04]# sed -r '/(^#|#|^$)/d' /home/hs/shell/shell_03/while_circulation.sh 
fun1 () {
read -p "username: " USER
while [ $USER != "root" ]
	do
		echo "账号错误"
		read -p "username: " USER
done	
}
while read i
	do
		echo "$i"
done < $1

# 

上面的这行 sed -r '/(^#|#|^$)/d' /home/hs/shell/shell_03/while_circulation.sh '

-r 是让后面支持正则表达式

/ ... / 这个是开启匹配模式

(^#|#|^$) 这个表示以#开头 或者是包含# 或者空行

d 这个命令是表示删除

最后的是文件名,所以上面一行代表的意思就是删除脚本中的注释与空行。

增加和删除都已经出现了,接下来是修改 命令是 s 和 c

# 修改有两种   替换与修改    首先是替换 将dog替换为cat
[root@VM-8-7-centos shell_04]# cat data.txt 
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.
# 命令格式是 sed 's/原字符串/新字符串/' 文件名 
[root@VM-8-7-centos shell_04]# sed 's/dog/cat/' data.txt 
1 the quick brown fox jumps over the lazy cat.
2 the quick brown fox jumps over the lazy cat.
3 the quick brown fox jumps over the lazy cat.
4 the quick brown fox jumps over the lazy cat.
5 the quick brown fox jumps over the lazy cat.
[root@VM-8-7-centos shell_04]# sed '3s/dog/cat/' data.txt          特定行
[root@VM-8-7-centos shell_04]# sed '2,4s/dog/cat/' data.txt 		一个范围的行数
[root@VM-8-7-centos shell_04]# sed '/3 the quick/s/dog/cat/' data.txt        匹配模式


# 修改    命令为c   将每一行的内容修改为
[root@VM-8-7-centos shell_04]# sed 'c\hello world' data.txt 
hello world
hello world
hello world
hello world
hello world
# 有一个小细节 如果是想修改2-4行   使用其他的方式 它就会把2-4行删除 然后重新新增一行
[root@VM-8-7-centos shell_04]# sed '2,4c\hello world' data.txt 
1 the quick brown fox jumps over the lazy dog.
hello world
5 the quick brown fox jumps over the lazy dog.

还有转换,将一个字母转换为另一个字母

[root@VM-8-7-centos shell_04]# cat data.txt 
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.
# 将a转换为A    b转换为B    这里注意 位置是一一对应的    中间字符串第一个只会转换为后面的字符串的第一个
[root@VM-8-7-centos shell_04]# sed 'y/abcde/ABCDE/' data.txt 
1 thE quiCk Brown fox jumps ovEr thE lAzy Dog.
2 thE quiCk Brown fox jumps ovEr thE lAzy Dog.
3 thE quiCk Brown fox jumps ovEr thE lAzy Dog.
4 thE quiCk Brown fox jumps ovEr thE lAzy Dog.
5 thE quiCk Brown fox jumps ovEr thE lAzy Dog.

将文件的内容打印到屏幕

[root@VM-8-7-centos shell_04]# sed 'p' data.txt 
1 the quick brown fox jumps over the lazy dog.
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

这里会出现重复的,因为这里将文件的内容打印到屏幕 还会将内存的数据打印到屏幕。


对文件的补充

sed命令格式:sed [命令选项] '{命令}[flag标志]' [文件名]

各个位置能够填写的参数是

#options选项
-e script 将脚本中指定的命令添加到处理输入时执行的命令中  多条件,一行中要有多个操作
-f script 将文件中指定的命令添加到处理输入时执行的命令中
-n        抑制自动输出
-i        编辑文件内容
-i.bak    修改时同时创建.bak备份文件。
-r        使用扩展的正则表达式
!         取反 (跟在模式条件后与shell有所区别)

#command   对文件干什么
sed常用内部命令
a   在匹配后面添加
i   在匹配前面添加
d   删除
s   查找替换  字符串
c   更改
y   转换   N D P 
p   打印



#flags
数字             表示新文本替换的模式
g:             表示用新文本替换现有文本的全部实例
p:             表示打印原始的内容
w filename:     将替换的结果写入文件

上面主要是将了命令相关的知识 还讲了命令选项-r 让后面支持正则表达式 接下来主要是将flag标志 这其实就是对命令的一个补充 还有一些其他的命令选项。

假如我现在将data.txt文件的内容改一下 结尾再加一个dog

1 the quick brown fox jumps over the lazy dog.dog
2 the quick brown fox jumps over the lazy dog.dog
3 the quick brown fox jumps over the lazy dog.dog
4 the quick brown fox jumps over the lazy dog.dog
5 the quick brown fox jumps over the lazy dog.dog

接下来对这个文件进行操作

现在继续将dog单词替换为cat

[root@VM-8-7-centos shell_04]# sed 's/dog/cat/' data.txt 
1 the quick brown fox jumps over the lazy cat.dog
2 the quick brown fox jumps over the lazy cat.dog
3 the quick brown fox jumps over the lazy cat.dog
4 the quick brown fox jumps over the lazy cat.dog
5 the quick brown fox jumps over the lazy cat.dog

这就会出现仅仅只是替换了第一个dog单词改为了cat。如果我想将读取到内充中的这一行中所有的dog都替换为cat如何实现?如果我仅仅替换第二次出现的dog又如何实现?这就需要使用到flag标志了。

# 直接在标志位写一个数字2即可实现将第二个dog替换为cat
[root@VM-8-7-centos shell_04]# sed 's/dog/cat/2' data.txt 
1 the quick brown fox jumps over the lazy dog.cat
2 the quick brown fox jumps over the lazy dog.cat
3 the quick brown fox jumps over the lazy dog.cat
4 the quick brown fox jumps over the lazy dog.cat
5 the quick brown fox jumps over the lazy dog.cat

# 如果在标志位使用g   这是将一行中所有的dog替换为cat
[root@VM-8-7-centos shell_04]# sed 's/dog/cat/g' data.txt 
1 the quick brown fox jumps over the lazy cat.cat
2 the quick brown fox jumps over the lazy cat.cat
3 the quick brown fox jumps over the lazy cat.cat
4 the quick brown fox jumps over the lazy cat.cat
5 the quick brown fox jumps over the lazy cat.cat

还可以打印输出 比如打印输出第三行

[root@VM-8-7-centos shell_04]# sed '3s/dog/cat/p' data.txt 
1 the quick brown fox jumps over the lazy dog.dog
2 the quick brown fox jumps over the lazy dog.dog
3 the quick brown fox jumps over the lazy cat.dog
3 the quick brown fox jumps over the lazy cat.dog
4 the quick brown fox jumps over the lazy dog.dog
5 the quick brown fox jumps over the lazy dog.dog

现在所有的修改都是针对内存来进行操作的,修改数据后真正的文件内容不会改变 如果想修改的内容让文件改变就在标志位写上 w

# 标志位 后面还需要写一个文件名  表示将修改后的内容保存至哪一个文件
[root@VM-8-7-centos shell_04]# sed 's/dog/cat/w data_copy.txt' data.txt 
1 the quick brown fox jumps over the lazy cat.dog
2 the quick brown fox jumps over the lazy cat.dog
3 the quick brown fox jumps over the lazy cat.dog
4 the quick brown fox jumps over the lazy cat.dog
5 the quick brown fox jumps over the lazy cat.dog
[root@VM-8-7-centos shell_04]# ls
data_copy.txt  data.txt
[root@VM-8-7-centos shell_04]# cat data_copy.txt 
1 the quick brown fox jumps over the lazy cat.dog
2 the quick brown fox jumps over the lazy cat.dog
3 the quick brown fox jumps over the lazy cat.dog
4 the quick brown fox jumps over the lazy cat.dog
5 the quick brown fox jumps over the lazy cat.dog

# 如果不写文件名是会报错的
[root@VM-8-7-centos shell_04]# sed 's/dog/cat/w' data.txt 
sed: couldn't open file : No such file or directory

上面是修改了第一个dog单词,如果我想修改一行中所有的dog单词 然后将修改的结果进行保存至另一个文件中,问题就是标志位的命令可不可使用多个,答案是可以的

[root@VM-8-7-centos shell_04]# sed 's/dog/cat/gw aa.txt' data.txt 
1 the quick brown fox jumps over the lazy cat.cat
2 the quick brown fox jumps over the lazy cat.cat
3 the quick brown fox jumps over the lazy cat.cat
4 the quick brown fox jumps over the lazy cat.cat
5 the quick brown fox jumps over the lazy cat.cat
[root@VM-8-7-centos shell_04]# ls
aa.txt  data.txt
[root@VM-8-7-centos shell_04]# cat aa.txt 
1 the quick brown fox jumps over the lazy cat.cat
2 the quick brown fox jumps over the lazy cat.cat
3 the quick brown fox jumps over the lazy cat.cat
4 the quick brown fox jumps over the lazy cat.cat
5 the quick brown fox jumps over the lazy cat.cat

我想能不能修改后的内容保存在原来的文件中,试了一下,结果导致原文件中的内容都没有了

[root@VM-8-7-centos shell_04]# sed 's/dog/cat/w data.txt' data.txt 

接下来是讲命令选项 ,命令选项是对sed命令的补充

首先是仅仅打印我修改了的数据,内存中读入的数据不打印 使用-n

[root@VM-8-7-centos shell_04]# sed -n '3s/dog/cat/p' data.txt 
3 the quick brown fox jumps over the lazy cat.dog
[root@VM-8-7-centos shell_04]# sed -n '3s/dog/cat/' data.txt 
[root@VM-8-7-centos shell_04]# 
# 加上-n就可以仅仅显示我们要打印的数据,如果没有在命令或者flag标志中选择p打印的话  -n最后是没有任何数据显示的

还可以执行多个命令,就比如我要将brown单词改为green dog单词改为cat 像这种需要执行多个命令的就需要使用 -e 中间用; 隔开

[root@VM-8-7-centos shell_04]# sed -e 's/brown/green/;s/dog/cat/g' data.txt 
1 the quick green fox jumps over the lazy cat.cat
2 the quick green fox jumps over the lazy cat.cat
3 the quick green fox jumps over the lazy cat.cat
4 the quick green fox jumps over the lazy cat.cat
5 the quick green fox jumps over the lazy cat.cat

还可以将命令写入到文件中,执行时直接读取文件中的命令来执行 需要使用-f

# 首先创建一个文件
[root@VM-8-7-centos shell_04]# vim command.txt

# 文件内容如下 还是上面的将brown单词改为green  dog单词改为cat  因为这里命令没有写在一行 所以中间就不用分号隔开
s/brown/grent/
s/dog/cat/g
           

# 然后在执行 command.txt为命令文件      data.txt为原数据文件
[root@VM-8-7-centos shell_04]# sed -f command.txt data.txt 
1 the quick grent fox jumps over the lazy cat.cat
2 the quick grent fox jumps over the lazy cat.cat
3 the quick grent fox jumps over the lazy cat.cat
4 the quick grent fox jumps over the lazy cat.cat
5 the quick grent fox jumps over the lazy cat.cat

修改源文件,上面使用命令flag标志w 只是将打印内容写入到另一个文件,现在是对源文件进行修改。

[root@VM-8-7-centos shell_04]# cat data.txt 
1 the quick brown fox jumps over the lazy dog.dog
2 the quick brown fox jumps over the lazy dog.dog
3 the quick brown fox jumps over the lazy dog.dog
4 the quick brown fox jumps over the lazy dog.dog
5 the quick brown fox jumps over the lazy dog.dog
[root@VM-8-7-centos shell_04]# sed -i 's/dog/cat/g' data.txt 
# 将所有的dog修改为cat 后屏幕是没有输出  但是再查看原文件内容已经发生了改变
[root@VM-8-7-centos shell_04]# cat data.txt 
1 the quick brown fox jumps over the lazy cat.cat
2 the quick brown fox jumps over the lazy cat.cat
3 the quick brown fox jumps over the lazy cat.cat
4 the quick brown fox jumps over the lazy cat.cat
5 the quick brown fox jumps over the lazy cat.cat




# 在日常中  修改源文件其实是很危险的操作 因为这是不可逆的 可以使用-i.xxx 先对原文件进行备份,然后再对原文件进行修改
# 现在的data.txt文件内容
[root@VM-8-7-centos shell_04]# cat data.txt
1 the quick brown fox jumps over the lazy cat.cat
2 the quick brown fox jumps over the lazy cat.cat
3 the quick brown fox jumps over the lazy cat.cat
4 the quick brown fox jumps over the lazy cat.cat
5 the quick brown fox jumps over the lazy cat.cat
# 现在进行修改操作
[root@VM-8-7-centos shell_04]# sed -i.0905 's/cat/dog/g' data.txt 
# 这个时候就会自动创建一个data.txt.0905文件
[root@VM-8-7-centos shell_04]# ls
command.txt  data.txt  data.txt.0905
# 修改后的data.txt文件
[root@VM-8-7-centos shell_04]# cat data.txt
1 the quick brown fox jumps over the lazy dog.dog
2 the quick brown fox jumps over the lazy dog.dog
3 the quick brown fox jumps over the lazy dog.dog
4 the quick brown fox jumps over the lazy dog.dog
5 the quick brown fox jumps over the lazy dog.dog
# 修改前进行备份的原文件
[root@VM-8-7-centos shell_04]# cat data.txt.0905 
1 the quick brown fox jumps over the lazy cat.cat
2 the quick brown fox jumps over the lazy cat.cat
3 the quick brown fox jumps over the lazy cat.cat
4 the quick brown fox jumps over the lazy cat.cat
5 the quick brown fox jumps over the lazy cat.cat

sed命令小技巧

统计data.txt文件有多少行

[root@VM-8-7-centos shell_04]# sed -n '$=' data.txt
5

对输出流处理

awk命令可以帮助我们从输出流中把我们想要的数据找出来并对这些数据进行处理,awk也是一个行编辑器命令,可以截取某一行中某一列的内容并对这些内容进行处理、运算、输出。

awk工作方式是读取数据,将每一行数据视为一条记录, 每条记录以分隔符分为若干个字段 行的分隔符为回车 字段的分割为一个或多个空格

就比如求内存的使用率,所有的内存数以及已使用的内存都在一行的中,如果需要将我们想要的数据检索出来并进行处理就可以使用awk命令

[root@VM-8-7-centos ~]# free
              total        used        free      shared  buff/cache   available
Mem:        1882008      458880       94056         592     1329072     1226400
Swap:             0           0           0

语法:

awk [option命令选项] '[BEGIN]{program}[END]' [fileName]

命令选项为:

  • -F fs 一行中各个字段的列分隔符 默认是空格 如果是其他的就需要使用该命令选项来指定分隔符
  • -f file 指定读取程序的文件名
  • -v var=value awk使用的变量与值

awk的命令program必须放在两个大括号中,并且必须用单引号将大括号引起来

awk的运行优先级:

  • BEGIN: 在开始处理数据流之前执行,可选项
  • program: 如何处理数据流,必选项
  • END: 处理完数据流后执行,可选项

awk命令基本用法

awk的基本用法就是数据的提取功能,能够从一行中提取某一个字段

首先创建一个文本文件

[root@VM-8-7-centos shell_04]# vim awk_data.txt

1 the quick brown fox jumps over the lazy cat . dog
2 the quick brown fox jumps over the lazy cat . dog
3 the quick brown fox         jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy cat . dog
5 the quick brown fox jumps over the lazy cat . dog

awk命令对列的提取

字段相关内置变量

$0 表示整行文本

$1 表示文本行中的第一个数据字段

$2 表示文本行中的第二个数据字段

$N 表示文本行中的第N个数据字段

$NF 表示文本行中的最后一个数据字段

# 提取整行内容   awk的打印输出命令是print
[root@VM-8-7-centos shell_04]# awk '{print $0}' awk_data.txt 
1 the quick brown fox jumps over the lazy cat . dog
2 the quick brown fox jumps over the lazy cat . dog
3 the quick brown fox         jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy cat . dog
5 the quick brown fox jumps over the lazy cat . dog

# 提取每行第三个字段的内容
[root@VM-8-7-centos shell_04]# awk '{print $3}' awk_data.txt 
quick
quick
quick
quick
quick

# 还可以打印多个字段 直接在{} 中写多个$ 即可 中间用逗号分割
[root@VM-8-7-centos shell_04]# awk '{print $1,$3,$5}' awk_data.txt 
1 quick fox
2 quick fox
3 quick fox
4 quick fox
5 quick fox

对行的提取

上面会了对列的提取,如果我想提取某一行 可以使用NR来指定,或者开启匹配模式

# 比如 提取第三行的整行内容
[root@VM-8-7-centos shell_04]# awk 'NR==3{print $0}' awk_data.txt 
3 the quick brown fox         jumps over the lazy cat . dog

[root@VM-8-7-centos shell_04]# awk '/2 the/{print $0}' awk_data.txt 
2 the quick brown fox jumps over the lazy cat . dog

如果列的分隔符不是空格的情形下

上面都是对awk_data.txt文件进行操作,这是一个标准的文件,字段与字段之间采用的是空格分割的,那如果采用的其他符号进行分隔符就需要在option命令选项使用-F

# 比如这个文件,字段与字段之间采用的冒号分割
[root@VM-8-7-centos shell_04]# cat /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
......


# 使用-F 指定字段分隔符
[root@VM-8-7-centos shell_04]# awk -F ":" '{print $1}' /etc/passwd
root
bin
daemon
adm
lp
sync
shutdown
halt
......

# 还可以将 awk -F ":"  写成 awk -F:   

改变输出后的字段分割符

打印的多个字段默认采用的是空格分割,还可以改变, 格式为:awk '{print $1 "双引号中写自定义的分隔符" $2}' 文件名

# 默认输出在屏幕后的字段是采用的空格分割
[root@VM-8-7-centos shell_04]# awk '{print $1,$3,$5}' awk_data.txt 
1 quick fox
2 quick fox
3 quick fox
4 quick fox
5 quick fox

# 改变分隔符后的输出
[root@VM-8-7-centos shell_04]# awk '{print $1 "--" $3 "--" $5}' awk_data.txt 
1--quick--fox
2--quick--fox
3--quick--fox
4--quick--fox
5--quick--fox

其实就是打印字符串,双引号引起来的就是字符串,如果没有引起来的就是变量

awk的三个优先级

BEGIN的优先级最高 最先执行 它是在从输出流中读取数据之前进行的操作 , 它不依赖于输出流 所以仅仅使用BEGIN允许最后没有文件名

END 的优先级最低 最后执行 它是在输出流中操作数据之后执行

[root@VM-8-7-centos shell_04]# awk 'BEGIN{print "begin...."}{print $0}END{print "end..."}' awk_data.txt 
begin....
1 the quick brown fox jumps over the lazy cat . dog
2 the quick brown fox jumps over the lazy cat . dog
3 the quick brown fox         jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy cat . dog
5 the quick brown fox jumps over the lazy cat . dog
end...


# 不需要数据源也可以输出
[root@VM-8-7-centos shell_04]# awk 'BEGIN{print "begin...."}'
begin....

# 没有提供数据源 所以无法输出
[root@VM-8-7-centos shell_04]# awk '{print "test...."}'

awk命令高级用法

awk是一门语言,可以定义变量,还可以定义数组,还可以进行运算,环境变量,流程控制。

定义变量,案例使用查看内存的使用率,如下:

# 第一个是总内存   第二个是剩余内存
[root@VM-8-7-centos shell_04]# head -2 /proc/meminfo 
MemTotal:        1882008 kB
MemFree:          123520 kB

[root@VM-8-7-centos shell_04]# head -2 /proc/meminfo | awk 'NR==1{t=$2}NR==2{f=$2;print (t-f)/t*100 "%"}'
93.4561%


# 定义变量还可以这样使用
[root@VM-8-7-centos shell_04]# awk 'BEGIN{name="胡尚";print name}'
胡尚

定义数组

[root@VM-8-7-centos shell_04]# awk 'BEGIN{array[1]="hushang";array[2]="21";print array[1],array[2]}'
hushang 21

awk运算

  • 赋值运算 =
  • 比较运算 > >= == != <= <
  • 数学运算 + - * / % ++ – **
  • 逻辑运算 && || !
  • 匹配运算 ~ !~ == !=
# 赋值运算就不说了  上面定义变量就是赋值运算

# 比较运算。如果比较的是字符串则按ascii编码顺序表比较。如果结果返回为真则用1表示,如果返回为假则用0表示
[root@VM-8-7-centos shell_04]# awk 'BEGIN{print "a" >= "b"}'
0

# 数学运算
[root@VM-8-7-centos shell_04]# awk 'BEGIN{print 100+20}'
120
[root@VM-8-7-centos shell_04]# awk 'BEGIN{print 100/3}'
33.3333
[root@VM-8-7-centos shell_04]# awk 'BEGIN{print 2**5}'        #  ---->  2^5
32
[root@VM-8-7-centos shell_04]# awk 'BEGIN{print 10%3}'
1


# 逻辑运算    还是一样 如果结果返回为真则用1表示,如果返回为假则用0表示
[root@VM-8-7-centos shell_04]# awk 'BEGIN{print "a" <= "b" && "b" >= "c"}'
0

下面是匹配运算,也是用的比较多的

# passwd文件如下
[root@VM-8-7-centos shell_04]# cat /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
...

# 使用 == 来进行精确匹配 只输出第一个字段为root的一行
[root@VM-8-7-centos shell_04]# awk -F ":" '$1=="root"{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash

# 还可以使用模糊匹配  只要第一个字段中包含ro的都会被打印出来
[root@VM-8-7-centos shell_04]# awk '$1 ~ "ro"{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
chrony:x:997:995::/var/lib/chrony:/sbin/nologin

环境变量

变量描述
FIELDWIDTHS以空格分隔的数字列表,用空格定义每个数据字段的精确宽度
FS输入字段分隔符号 数据源的字段分隔符 -F
OFS输出字段分隔符号
RS输入记录分隔符
ORS输出记录分隔符号
# 首先是第一个环境变量 第一行是先输出/etc/passwd文件的第一行内容
[root@VM-8-7-centos shell_04]# awk 'NR==1{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
# 使用FIELDWIDTHS变量后的结果
[root@VM-8-7-centos shell_04]# awk 'BEGIN{FIELDWIDTHS="5 2 8"}NR==1{print $1,$2,$3}' /etc/passwd
root: x: 0:0:root

# FS 和 OFS 两个环境变量
[root@VM-8-7-centos shell_04]# awk 'NR==1{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@VM-8-7-centos shell_04]# awk 'BEGIN{FS=":"}NR==1{print $1,$3,$5}' /etc/passwd
root 0 root
[root@VM-8-7-centos shell_04]# awk 'BEGIN{FS=":";OFS="-"}NR==1{print $1,$3,$5}' /etc/passwd
root-0-root

# 剩下两个就是一行 输入的默认分隔符与输出到屏幕的行分隔符。

awk流程控制与循环

if语句

# 首先是查看文件
[root@VM-8-7-centos shell_04]# cat awk_data.txt 
1 the quick brown fox jumps over the lazy cat . dog
2 the quick brown fox jumps over the lazy cat . dog
3 the quick brown fox         jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy cat . dog
5 the quick brown fox jumps over the lazy cat . dog

# 打印第一个字段小于3的数据
[root@VM-8-7-centos shell_04]# awk '{if($0 < 3)print $0}' awk_data.txt 
1 the quick brown fox jumps over the lazy cat . dog
2 the quick brown fox jumps over the lazy cat . dog
# 上面是写在一行中 还可以通过shift+enter 写在多行
[root@VM-8-7-centos shell_04]# awk '{
> if($1<3)
> print $0
> }' awk_data.txt
1 the quick brown fox jumps over the lazy cat . dog
2 the quick brown fox jumps over the lazy cat . dog

# 但是不理解为什么下面 > 3 的条件竟然将第三行输出了
[root@VM-8-7-centos shell_04]# awk '{if($0 > 3)print $0}' awk_data.txt 
3 the quick brown fox         jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy cat . dog
5 the quick brown fox jumps over the lazy cat . dog
[root@VM-8-7-centos shell_04]# awk '{if($0 >= 3)print $0}' awk_data.txt 
3 the quick brown fox         jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy cat . dog
5 the quick brown fox jumps over the lazy cat . dog

if…else…语句

[root@VM-8-7-centos shell_04]# awk '{
> if($1<3)
>   print $1*2
> else
>   print $1/2
> '} awk_data.txt
2
4
1.5
2
2.5

for循环

首先创建一个文件,保存几个数字

[root@VM-8-7-centos shell_04]# vim number.txt

60 50 100
150 30 10
70 100 40

求和

# 首先使用-v命令选项定义一个变量 然后进行循环 最后使用end进行输出
[root@VM-8-7-centos shell_04]# awk -v "sum=0" '{for(i=1;i<4;i++){sum+=$i}}END{print sum}' number.txt 
610

# 在begin中定义变量也可以
[root@VM-8-7-centos shell_04]# awk 'BEGIN{sum=0}{for(i=1;i<4;i++){sum+=$i}}END{print sum}' number.txt 
610

# 使用多行的格式
[root@VM-8-7-centos shell_04]# awk -v "sum=0" '{
> for(i=1;i<4;i++){
>   sum+=$i
> }}END{print sum}' number.txt
610

while循环

使用while循环 输出每行的几个字段的和

[root@VM-8-7-centos shell_04]# cat number.txt 
60 50 100
150 30 10
70 100 40

[root@VM-8-7-centos shell_04]# awk '{
> i=1
> sum=0
> while(i<4){
>     sum+=$i
>     i++
> }
> print sum}' number.txt
210
190
210

还有一个do…while格式。先执行循环在进行判断

[root@VM-8-7-centos shell_04]# awk '{
> sum=0
> i=1
> do{
>  sum+=$i
>  i++
> }while(i<4)
> print sum}' number.txt
210
190
210

循环控制语句

break

awk小技巧

# 打印行数
[root@VM-8-7-centos shell_04]# awk 'END{print NR}' number.txt 
3

# 打印最后一行内容
[root@VM-8-7-centos shell_04]# awk 'END{print $0}' number.txt 
70 100 40

# 打印列数
[root@VM-8-7-centos shell_04]# awk 'END{print NF}' number.txt 
3


脚本案例


监控一个主机的存活状态

监控方法采用ping 使用的是icmp协议。如果能ping通则表示主机存活 如果ping不通则主机死亡

关于该知识点的一些问题

  1. 关于禁ping 防止DDOS攻击

    这里主机如果是禁ping 禁的是陌生人 。 禁止所有但是允许你自己的ip进行ping的操作

  2. 网络有延迟,可能因为网络的原因产生假报警问题,

    需要设置ping的报警阀值,假如三次全部失败就报警 主机down

  3. ping的频率

    1-5秒ping一次都可以 但是不要毫秒级单位去ping

#!/usr/bin/bash
# Author: hushang
# Cleared Time: 2021-09-07 19:56
# Script Description: 监控目标主机存活状态

for ((i=1;i<4;i++));do
# 测试代码   -c1  表示就发生一次ping  不管能否ping通都不输出结果  然后定义变量
        if ping -c1 $1 &>/dev/null;then
                export ping_status"$i"=1
        else
                export ping_status"$i"=0
        fi

# 时间间隔 1秒1次
        sleep 1
done

# 3次都ping成功才算成功
if [ $ping_status1 -eq $ping_status2 ] && [ $ping_status2 -eq $ping_status3 ] && [ $ping_status1 -eq 0 ];then
        echo "$1 is down"
else
        echo "$1 is up"
fi

# 撤销上面定义的全局变量
unset ping_status1
unset ping_status2
unset ping_status3
                   

监控端口存活状态

比较常见的监控方法是:

  • 通过systemctl 或 service 命令来查看服务启动状态
  • 通过lsof命令 查看端口是否存在
  • 通过ps命令 查看进程

上面这三种都有可能出现 服务down了上述的一些东西还在 或者是 压力过大 服务无法响应

这里推荐使用 telnet命令测试端口是否有响应

如果没有该命令 那就安装一下

# 安装命令
yum -y install telnet

# 测试一下连接当前服务器的22号端口
[root@VM-8-7-centos shell_05]# telnet 82.156.9.191 22
Trying 82.156.9.191...
Connected to 82.156.9.191.
Escape character is '^]'.
SSH-2.0-OpenSSH_7.4
# 然后输入quit退出
# 如果能够出现 '^]'  符号就表示该端口是开放的  如果端口没有开放就会提示connection 
# 这里有个小问题 如果我测试一个没有开放的端口 就会一直等待 不会有提示信息输出  可是视频教程有一个连接超时的提示

# 该命令的位置
[root@VM-8-7-centos shell_05]# which telnet
/usr/bin/telnet

补充知识点——创建临时文件

mktemp 文件名.XXXXXX     # 可以有多个X  但至少要有三个

# 现有的文件
[root@VM-8-7-centos shell_05]# ls
host_status.sh  port_status.sh
# 创建一个临时文件
[root@VM-8-7-centos shell_05]# mktemp test.XXX
test.uL5
# 创建后就会看到多了一个文件
[root@VM-8-7-centos shell_05]# ls
host_status.sh  port_status.sh  test.uL5
# 再创建一次
[root@VM-8-7-centos shell_05]# mktemp test.XXXXXXX
test.pikvvR9
[root@VM-8-7-centos shell_05]# ls
host_status.sh  port_status.sh  test.pikvvR9  test.uL5
# 后面写几个X 就会随机生成几位

# 还可以创建临时文件夹, 使用-d命令选项
mktemp -d test.XXXXXXX

脚本如下:

#!/usr/bin/bash
# Author: hushang
# Created Time: 2021-09-08 12:45
# Script Description: 监控一个主机的端口

port_status () {

# 首先判断一下/usr/bin/telnet这个可执行文件是否存在
if [ ! -f /usr/bin/telnet ];then
        echo "not fount command : telnet"
        exit 1
fi

# 创建一个临时文件 用来保存执行telnet命令后的输出内容
TEMP_FILE=`mktemp port_status.XXXXX`

# 开始测试端口是否存在   $1代表IP地址  $2代表端口
# 然后将执telnet命令 输出在屏幕的结果保存在上面创建的一个临时文件
(telnet $1 $2 <<EOF
quit
EOF
) &> $TEMP_FILE

# 使用grep -e 通过检索这个临时文件 是否包含 ^]  字符
# &>/dev/null 这个是不需要将检索临时文件的结果输出在屏幕上
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

监控内存使用率

首先是查看内存使用情况

# total总共内存   used已使用      free剩余    shered共享内存      buff缓冲
[root@VM-8-7-centos ~]# free -m
              total        used        free      shared  buff/cache   available
Mem:           1837         448         122           0        1267        1197
Swap:             0           0           0

# 第二个命令就是:
[root@VM-8-7-centos shell_05]# cat /proc/meminfo
MemTotal:        1882008 kB
MemFree:          126520 kB
MemAvailable:    1229092 kB
Buffers:          125948 kB
Cached:          1048056 kB
SwapCached:            0 kB
.....

在这个里面 内存使用的优先级是 free —> cache —> buffer ----> Swap

当used的值不高 free的值不高 buff的值高 这种情况并不是内存紧张 ,只是很多东西读入到内存中 没有清空缓存而已,没有释放到free

当used的值高 free的值不高 buff的值也不高 这种情况才是内存紧张了

上面两种命令随便选择一种使用都可以,这里采用第二种

# 统计已使用的内存占总内存的百分比
[root@VM-8-7-centos shell_05]# head -2 /proc/meminfo 
MemTotal:        1882008 kB
MemFree:          126280 kB
[root@VM-8-7-centos shell_05]# head -2 /proc/meminfo | awk 'NR==1{t=$2}NR==2{f=$2;print (t-f)/t*100"%"}'
93.3059%

# 统计cache的使用率
[root@VM-8-7-centos shell_05]# head -5 /proc/meminfo
MemTotal:        1882008 kB
MemFree:          126036 kB
MemAvailable:    1229168 kB
Buffers:          126052 kB
Cached:          1048504 kB
[root@VM-8-7-centos shell_05]# head -5 /proc/meminfo | awk 'NR==1{t=$2}NR==5{c=$2;print c/t*100"%"}'
55.7171%

# 统计buffer的使用率
[root@VM-8-7-centos shell_05]# head -5 /proc/meminfo | awk 'NR==1{t=$2}NR==4{b=$2;print b/t*100"%"}'
6.69902%

脚本如下:

#!/usr/bin/bash
# Author: hushang
# Created Time: 2021-09-09 13:04
# Script Description: 监控内存的使用率

memory_use(){
        memory_used=`head -2 /proc/meminfo | awk 'NR==1{t=$2}NR==2{f=$2;print (t-f)/t*100"%"}'`
        memory_cache=`head -5 /proc/meminfo | awk 'NR==1{t=$2}NR==5{c=$2;print c/t*100"%"}'`
        memory_buff=`head -5 /proc/meminfo | awk 'NR==1{t=$2}NR==4{b=$2;print b/t*100"%"}'`

        echo "memory used: $memory_used"
        echo "memory cache used: $memory_cache"
        echo "memory buff used: $memory_buff"
}                               
                                
memory_use

使用cpu或内存前十的进程

目的:通过监控找出一些非法占用内存高的进程,发现后停止该进程

监控方法:

pstop 命令 这两个命令的区别是ps的静态的命令 显示上一秒的进程情况,而 top 命令是动态的,显示的数值会动态的改变。

通过对任务管理器中的进程对内存或cpu的使用情况进行整合、排序

使用上面两个命令 其实显示的内容都差不多

[root@VM-8-7-centos shell_05]# ps -aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1  43612  3672 ?        Ss   Jun25  10:38 /usr/lib/systemd/systemd --system --deserialize 20
root         2  0.0  0.0      0     0 ?        S    Jun25   0:00 [kthreadd]
root         4  0.0  0.0      0     0 ?        S<   Jun25   0:00 [kworker/0:0H]
root         6  0.0  0.0      0     0 ?        S    Jun25   3:32 [ksoftirqd/0]
root         7  0.0  0.0      0     0 ?        S    Jun25   0:00 [migration/0]
root         8  0.0  0.0      0     0 ?        S    Jun25   0:00 [rcu_bh]
......

# 需要用到的也就是RSS 这表示那个进程使用了多少物理内存


# -n1  表示就打印一次    RES的值也就表示进程使用了多少物理内存
[root@VM-8-7-centos shell_05]# top -n1
top - 20:24:15 up 76 days, 10:03,  1 user,  load average: 0.90, 0.82, 0.63
Tasks: 107 total,   2 running, 105 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1882008 total,   181044 free,   458872 used,  1242092 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  1226416 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                         
 1047 root      20   0   50088    916    584 R  6.7  0.0   1321:32 rshim                                           
    1 root      20   0   43612   3672   2212 S  0.0  0.2  10:39.09 systemd                                         
    2 root      20   0       0      0      0 S  0.0  0.0   0:00.31 kthreadd                                       
    4 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 kworker/0:0H                                   
    6 root      20   0       0      0      0 S  0.0  0.0   3:32.11 ksoftirqd/0                                     
    7 root      rt   0       0      0      0 S  0.0  0.0   0:00.00 migration/0
# 将top命令打印在屏幕的内容输入至一个临时文件中
[root@VM-8-7-centos shell_05]# top -n1 > temp.txt
# 查看该文件 内容如下
[root@VM-8-7-centos shell_05]# cat temp.txt 
top - 20:37:39 up 76 days, 10:16,  1 user,  load average: 0.55, 0.44, 0.52
Tasks: 105 total,   2 running, 103 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1882008 total,   180920 free,   457248 used,  1243840 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  1227996 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND  
11345 rabbitmq  20   0 1827504  84188   4716 S  6.2  4.5 227:39.43 beam.smp  
    1 root      20   0   43612   3672   2212 S  0.0  0.2  10:39.46 systemd  
    2 root      20   0       0      0      0 S  0.0  0.0   0:00.31 kthreadd 
    4 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 kworker/0:0H
    6 root      20   0       0      0      0 S  0.0  0.0   3:32.19 ksoftirqd/0  
    7 root      rt   0       0      0      0 S  0.0  0.0   0:00.00 migration/0 
    8 root      20   0       0      0      0 S  0.0  0.0   0:00.00 rcu_bh    
    9 root      20   0       0      0      0 S  0.0  0.0  10:01.43 rcu_sched 
   10 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 lru-add-drain 
   11 root      rt   0       0      0      0 S  0.0  0.0   0:14.90 watchdog/0  
   13 root      20   0       0      0      0 S  0.0  0.0   0:00.00 kdevtmpfs 
   14 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 netns  
   15 root      20   0       0      0      0 S  0.0  0.0   0:01.22 khungtaskd 
   16 root       0 -20       0      0      0 S  0.0  0.0   0:00.02 writeback  
   17 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 kintegrityd
   18 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 bioset         
   19 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 bioset 
   20 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 bioset
   21 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 kblockd 
   22 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 md    
   23 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 edac-poller     
   24 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 watchdogd   
   30 root      20   0       0      0      0 S  0.0  0.0   0:02.99 kswapd0  
   31 root      25   5       0      0      0 S  0.0  0.0   0:00.00 ksmd      
   32 root      39  19       0      0      0 S  0.0  0.0   0:06.20 khugepaged   
   33 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 crypto                                         
   41 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 kthrotld                                       
   43 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 kmpath_rdacd                                   
   44 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 kaluad                                         
   45 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 kpsmoused                                       
   46 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 ipv6_addrconf                                   
   59 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 deferwq                                         
   98 root      20   0       0      0      0 S  0.0  0.0   0:23.00 kauditd                                         
  193 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 iscsi_eh                                       
  245 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 ata_sff                                         
  251 root      20   0       0      0      0 S  0.0  0.0   0:00.00 scsi_eh_0                                       
  252 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 scsi_tmf_0 

但是如果我要创建一个脚本 所需要使用的内容是从第8行开始 那我如何让内容从第八行开始显示嘞? 这里可以使用tail命令

# 查看最后4行  如果想从头开始就将数字写我 +n
[root@VM-8-7-centos shell_05]# tail -n -5 temp.txt 
  193 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 iscsi_eh                                       
  245 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 ata_sff                                         
  251 root      20   0       0      0      0 S  0.0  0.0   0:00.00 scsi_eh_0                                       
  252 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 scsi_tmf_0 
  
# 改为+8就表示从第八行开始展示  
[root@VM-8-7-centos shell_05]# tail -n +8 temp.txt 
11345 rabbitmq  20   0 1827504  84188   4716 S  6.2  4.5 227:39.43 beam.smp 
    1 root      20   0   43612   3672   2212 S  0.0  0.2  10:39.46 systemd 
    2 root      20   0       0      0      0 S  0.0  0.0   0:00.31 kthreadd 
    4 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 kworker/0:0H 
    6 root      20   0       0      0      0 S  0.0  0.0   3:32.19 ksoftirqd/0 
    7 root      rt   0       0      0      0 S  0.0  0.0   0:00.00 migration/0 
    8 root      20   0       0      0      0 S  0.0  0.0   0:00.00 rcu_bh 
    9 root      20   0       0      0      0 S  0.0  0.0  10:01.43 rcu_sched 
   10 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 lru-add-drain  
   11 root      rt   0       0      0      0 S  0.0  0.0   0:14.90 watchdog/0 
   13 root      20   0       0      0      0 S  0.0  0.0   0:00.00 kdevtmpfs  
   14 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 netns 
   15 root      20   0       0      0      0 S  0.0  0.0   0:01.22 khungtaskd 
   16 root       0 -20       0      0      0 S  0.0  0.0   0:00.02 writeback 
   17 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 kintegrityd  
   18 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 bioset
   19 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 bioset
   20 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 bioset 
   
# 然后就使用awk命令定义一个数组 数组的名字就使用最后一个字段   数组的值就使用第六列 RES的值 文件内容有点问题 所以显示有点问题
[root@VM-8-7-centos shell_05]# tail -n +8 temp.txt | awk '{array[$13]=$6}END{for (i in array) print i,array[i]}'
 
scsi_eh_0 0
kpsmoused 0
md 0
kintegrityd 0
systemd 43612
kauditd 0
kmpath_rdacd 0
migration/0 0
scsi_tmf_0 0
iscsi_eh 0
crypto 0
writeback 0
kthrotld 0
 84188
khungtaskd 0
watchdog/0 0
rcu_sched 0
ksmd 0
edac-poller 0
kblockd 0
bioset 0
lru-add-drain 0
kaluad 0
kswapd0 0
watchdogd 0
netns 0
kthreadd 0
ata_sff 0
rcu_bh 0
deferwq 0
khugepaged 0
kdevtmpfs 0
kworker/0:0H 0
ipv6_addrconf 0
ksoftirqd/0 0

# 将内容进行排序 sort -k排序命令   2表示按第二列排序    -n表示数字     -r表示降序
# 最后再显示前面十行
[root@VM-8-7-centos shell_05]# tail -n +8 temp.txt | awk '{array[$13]=$6}END{for (i in array) print i,array[i]}' | sort -k 2 -n -r | head -10
 84188
systemd 43612
writeback 0
watchdogd 0
watchdog/0 0
scsi_tmf_0 0
scsi_eh_0 0
rcu_sched 0
rcu_bh 0
netns 0

文件内容的一些列字段有问题,但方法是这样的:

  1. 首先使用ps或top命令将内容保存在一个临时文件中
  2. 使用tail -n +数字 从第一行开始显示文件中的内容
  3. 然后在使用awk命令使用一个数组来存放每个进行使用的内容数
  4. 然后使用sort 命令进行排序
  5. 最后使用head命令打印前10行数据。

有个问题:现在 top命令并没有将所有的进程都打印出来 需要加一个 -b 命令选项

[root@VM-8-7-centos shell_05]# top -b -n 1 > temp.txt 
# 这里最后一个列 将$13改为了 $NF 
[root@VM-8-7-centos shell_05]# tail -n +8 temp.txt | awk '{array[$NF]=$6}END{for (i in array) print i,array[i]}' | sort -k 2 -n -r | head -10
beam.smp 84188
systemd-journal 59880
dockerd 55516
rsyslogd 40840
firewalld 29924
containerd 29440
tuned 18540
barad_agent 14076
iscsid 10124
polkitd 9500

具体脚本如下:

#!/usr/bin/bash
# Author: hushang
# Created Time: 2021-09-09 21:32
# Script Description: 统计使用内容最大的前十个进程

memory(){
    # 首先创建一个临时文件,然后将top命令输出的内容保存至临时文件中
    memory_temp_file=`mktemp memory_use.XXXX`
    top -b -n 1 > $memory_temp_file

    # 统计内存使用情况
    tail -n +8 $memory_temp_file | awk '{array[$NF]=$6}END{for (i in array) print array[i],i}' | sort -k 1 -n -r | head -10

    # 删除临时文件
    rm -f $memory_temp_file
}

memory



# 结果如下
[root@VM-8-7-centos shell_05]# bash memory_use2.sh 
84188 beam.smp
61240 systemd-journal
55516 dockerd
41532 rsyslogd
29924 firewalld
29440 containerd
18540 tuned
14076 barad_agent
10124 iscsid
9500 polkitd

# 如果的统计cpu的话 改变一个字段即可 将awk命令中的$6 变为 $9 即可
相关推荐

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

胡尚

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值