2 Star 17 Fork 10

深蓝 / 编程之路

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
Shell编程.md 80.89 KB
一键复制 编辑 原始数据 按行查看 历史
深蓝 提交于 2021-11-25 23:46 . Shell编程

Shell编程

1. 常用Linux命令

数据检索命令:

​ 行检索:grep egrep

​ 字符串检索:cut tr

数据处理命令:

​ 数据排序:sort

​ 数据去重:uniq

​ 文本数据合并:paste

​ 数据输出:tee

​ 数据处理:xargs

1.1 数据处理命令

1.1.1 grep过滤

负责从数据源中检索对应的字符串,根据关键字进行行过滤

语法:grep options 'keys' filename
OPTIONS:
	-i:不区分大小写
	-v: 查找不包含指定内容的行,反向选择
	-w: 按单词搜索
	-n: 显示行号
	-A: 显示匹配行及后面多少行 -A 5
	-B: 显示匹配行及前面多少行
	
	-o: 打印匹配关键字
	-c: 统计匹配到的次数
	-r: 逐层遍历目录查找
	-C: 显示匹配行前后多少行
	-l: 只列出匹配的文件名
	-L: 列出不匹配的文件名
	-e: 使用正则匹配
	-E: 使用扩展正则匹配
	^key: 以关键字开头
	key$: 以关键字结尾
	^$: 匹配空行
	--color=auto: 可以将找到的关键词部分加上颜色的显示
	
常用示例:
grep -i root passwd	忽略大小写匹配包含root的行
grep -w ftp passwd 精确匹配ftp单词
grep -wo ftp passwd 打印匹配到的关键字ftp
grep -n root passwd 打印匹配到root的行号
grep -in root passwd 忽略大小写统计包含关键字root的行
grep -nic root passwd 忽略大小写匹配统计包含关键字root的行数
grep -i ^root passwd 忽略大小写匹配以root开头的行
grep bash$ passwd 匹配以bash结尾的行
grep -n ^$ passwd 匹配空行并打印行号
grep ^/etc/vsftpd/vsftpd.conf 匹配以#号开头的行
grep -v ^/etc/vsftpd/vsftpd.conf 匹配不以#号开头的行
grep -A 5 mail passwd 匹配包含mail关键字及其后5行
grep -B 5 mail passwd 匹配包含mail关键字及其前5行
grep -C 5 mail passwd 匹配包含mail关键字及其前后5行

1.1.2 cut数据截取

cut 用于列截取
-c: 以字符为单位进行分割
-d: 自定义分隔符,默认为制表符 \t
-f: 与-d一起使用,指定显示哪个区域

# cut -d: -f1 1.txt 以:冒号分割,截取第1列内容
# cut -d: -f1,6,7 1.txt 以:冒号分割,截取第1,6,7列内容
# cut -c4 1.txt 截取文件中每行第4个字符
# cut -c1-4 1.txt 截取文件中每行的1-4个字符
# cut -c4-10 1.txt # cut -c5- 1.txt 从第5个字符开始截取后面所有字符

1.1.3 tr字符转换

  • 从标准输入中通过替换或删除进行字符转换
  • 主要用于删除文件中的控制字符或进行字符转换
  • 使用tr要转换两个字符串:字符串1用于查询,字符串2用于处理各种转换
语法:
commands|tr 'string1' 'string2'
tr 'string1' 'string2' < filename
tr options 'string1' < filename

-d 删除字符串1中所有输入字符
-s 删除所有重复出现字符序列,只保留第一个【将重复出现的字符串压缩为一个字符串】

a-z 任意小写
A-Z 任意大写
0-9 任意数字

# tr -d '[:/]' < 3.txt 删除文件中的:和/
# cat 3.txt |tr -d '[:/]' 删除文件中的:和/
# tr '[0-9]' '@' < 3.txt 将文件中的数字替换为@符号
# tr '[a-z]' '[A-Z]' < 3.txt 将文件中的小写字母替换成大写字母
# tr -s '[a-z]' < 3.txt 匹配小写字母并将重复的压缩为一个
# tr -s '[a-z0-9]' < 3.txt 匹配小写字母和数字并将重复的压缩为一个

1.1.4 sort排序

将文件的每一行作为一个单位,从首字符向后,依次按ASCII码值进行比较,最后将他们按升序输出。

语法:
sort [options] [filename]

-u 去除重复行
-r 降序排序,默认是升序
-o 将排序结果输出到文件中 类似 重定向符号 > 
-n 以数字排序,默认是按字符排序
-t 分隔符
-k 第N列

-b 忽略前导空格
-R 随机排序,每次运行的结果均不同

 示例:
# sort -n -t: -k3 1.txt 按照用户的uid进行升序排列
# sort -nr -t: -k3 1.txt 按照用户的uid进行降序排列
# sort -n 2.txt 按照数字排序
# sort -nu 2.txt 按照数字排序并且去重
# sort -nr 2.txt 
# sort -nru 2.txt 
# sort -nru 2.txt 
# sort -n 2.txt -o 3.txt 按照数字排序并将结果重定向到文件
# sort -R 2.txt 
# sort -u 2.txt 

1.1.5 uniq去除连续的重复行

技巧:去重前先使用sort排序

语法:
uniq [options] [filename]

-i 忽略大小写
-c 统计重复行次数
-d 只显示重复行

uniq 2.txt
uniq -d 2.txt
uniq -dc 2.txt

1.1.6 tee双向输出

tee工具从标准输入读取并写入标准输出和文件,即:双向覆盖重定向<屏幕输出|文本输入>
somecommand | tee filename

-a 双向追加重定向

# echo hello world
# echo hello world|tee file1
# cat file1 
# echo 999|tee -a file1
# cat file1 

1.1.7 paste

合并文件行,输出到屏幕,不改动源文件

-d:自定义间隔符,默认是tab,只接受一个字符
-s:将每个文件中的所有内容按照一行输出,文件中的行与行以TAB间隔。

1.1.8 xargs

  • 上一个命令的输出作为下一个命令的命令行参数
  • 可以将管道或标准输入数据转换成命令行参数,也能够从文件的输出中读取数据
  • 一般和管道使用
  • 管道【|】上一个命令的输出作为下一个命令的输入,做的是数据源
命令格式:
''[somecommand]|[filename]'' | xargs -item command
 
OPTIONS:
-a file 从文件中读入作为sdtin
-E flag flag必须是一个以空格分隔的标志,当xargs分析到含有flag这个标志的时候就停止
-p 当每次执行一个argument的时候询问一次用户
-n num后面加次数,表示命令在执行的时候一次用的argument的个数,默认是用所有的
-t 表示先打印命令,然后再执行
-i  或者是-I,这得看linux支持了,将xargs的每项名称,一般是一行一行赋值给 {},可以用 {} 代替
-r no-run-if-empty 当xargs的输入为空的时候则停止xargs,不用再去执行了
-d delim 分隔符,默认的xargs分隔符是回车,argument的分隔符是空格,这里修改的是xargs的分隔符

1.2 shell字符

! 执行历史命令
$ 变量中取内容符
+ - * / % 数学运算 加 减 乘 除 取余
& 后台执行
; 在shell中执行多个命令,用;分隔
\ 转义字符
`` 命令中执行命令 ehco "today is `date +%F`"
'' 标示字符串 不解释变量
"" 标示字符串 解释变量
 
通配符
~ 家目录
* 匹配所有
? 匹配除回车以外的一个字符
[list] 匹配list中的任意单个字符
[!list] 匹配除list中的任意单个字符
{string1,string2,...} 匹配string1,string2或更多字符串
 
重定向
> 覆盖输入
>> 追加输入
< 输出
<< 追加输出
 
管道命令
| 上一个命令的输出作为下一个命令的输入 cat filename | grep 'abc'

1.3 组合命令实战

检索本机的IP、NetMask、MAC地址、广播地址

[root@VM-0-6-centos study]# ifconfig eth0 | grep -w 'inet' | tr -s ' ' | cut -d ' ' -f 3 | xargs echo "IP: "
IP:  172.16.0.6
[root@VM-0-6-centos study]# ifconfig eth0 | grep -w 'inet' | tr -s ' ' | cut -d ' ' -f5 | xargs echo "NETMASK: "
NETMASK:  255.255.240.0
[root@VM-0-6-centos study]# ifconfig eth0 | grep -w 'inet' | tr -s ' ' | cut -d ' ' -f 7 | xargs echo "BROADCAST: "
BROADCAST:  172.16.15.255
[root@VM-0-6-centos study]# ifconfig eth0 | grep -w "ether" | tr -s ' ' | cut -d ' ' -f 3 | xargs echo "MAC: "
MAC:  52:54:00:3f:fb:ba

将系统中所有普通用户的用户名、密码和默认shell保存到一个文件中,要求用户名密码和默认shell之间用tab分隔

grep -i 'bash' /etc/passwd | grep -v 'root' | cut -d ':' -f 1,2,7 | tr ':' '\t'

1.4 应用技巧

  • 先截取行
  • 统一数据格式
  • 截取字符串
  • 处理字符串

2. shell简介

2.1 简介

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

image-20211105152056996

2.2 快捷键

ctrl + c 终止前台运行的程序
ctrl + z 将前台运行的程序挂起到后台
ctrl + d 退出 等价exit
ctrl + l 清屏
ctrl + a | home 光标移到命令行的最前端
ctrl + e | end 光标移到命令行的后端
ctrl + u 删除光标前所有字符
ctrl + k 删除光标后所有字符
ctrl + r 搜索历史命令

2.3 例子

shell脚本 == 若干命令 + 脚本的基本格式 + 脚本特定语法 + 思想

脚本命令演示

创建一个用户:zipeng ==> useradd zipeng

密码设置为:coding.com ==> echo 'coding.com' | passwd --stdin zipeng

该用户创建文件夹:/study/zipeng ==> mkdir /study/zipeng

该用户创建文件:/study/zipeng/README ==> touch /study/zipeng/README

将'hello world'输入到/study/zipeng/README ==> echo 'hello world' > /study/zipeng/README

实现:

#!/bin/bash

# DESC: this is a test script
# AUTHOR: li zipeng
# RELEASE: 1.0

# main

# 创建用户 zipeng
useradd zipeng
# 设置密码 coding.com
echo 'coding.com' | passwd --stdin zipeng
# 该用户创建文件夹:/study/zipeng
su - zipeng -c "mkdir /study/zipeng"
# 该用户创建文件:/study/zipeng/README
su - zipeng -c "touch /study/zipeng/README"
# 将'hello world'输入到/study/zipeng/README
su - zipeng -c "echo 'hello world' > /study/zipeng/README"

3. shell脚本语法

3.1 脚本组成

image-20211106084142660

3.2 脚本命名

  • xxx.sh
  • 控制在26个字节内

3.3代码规范

  • #!/bin/bash
    • 脚本第一行
    • #!为魔法字符,指定脚本代码执行的程序【使用哪一种shell】
  • # 代表注释,而#!是特例
  • 脚本描述信息,方便后续使用与维护
    • # Name: 脚本名字
    • # Desc: 脚本描述
    • # Path: 存放路径
    • # Usage: 用法
    • # Update: 更新时间
    • # Author: 作者
    • # Release: 分发版本

3.4 执行方式

3.4.1 标准执行

[root@VM-0-6-centos study]# cat 1.sh
#!/bin/bash

# DESC: this is a test script
# AUTHOR: li zipeng
# RELEASE: 1.0

# main

# 创建用户 zipeng
useradd zipeng
# 设置密码 xxx
echo 'xxx' | passwd --stdin zipeng
# 该用户创建文件夹:/study/zipeng
su - zipeng -c "mkdir /study/zipeng"
# 该用户创建文件:/study/zipeng/README
su - zipeng -c "touch /study/zipeng/README"
# 将'hello world'输入到/study/zipeng/README
su - zipeng -c "echo 'hello world' > /study/zipeng/README"


[root@VM-0-6-centos study]# chmod +x 1.sh
[root@VM-0-6-centos study]# ll
-rwxr-xr-x 1 root root  470 Nov  5 15:44 1.sh

[root@VM-0-6-centos study]# ./1.sh
Changing password for user zipeng.
passwd: all authentication tokens updated successfully.
mkdir: cannot create directory ‘/study/zipeng’: Permission denied
touch: cannot touch ‘/study/zipeng/README’: No such file or directory
-bash: /study/zipeng/README: No such file or directory

3.4.2非标准执行

# 如果脚本没有加可执行权限,则不能使用 bash 1.sh
bash 1.sh
sh 1.sh
bash -x 1.sh

-x 一般用于排错,查看脚本执行过程
-n 用来查看脚本的语法是否有问题

3.4.3 命令式执行

定义命令路径变量 PATH
PATH=$PATH:脚本路径

备注:脚本必须给予执行权限

3.4.4 其他执行

source 1.sh
. 1.sh

source . 表示读取文件,执行文件里的命令

4. shell变量详解

4.1变量定义

4.1.1 命名规则

  • 在shell编程中变量和等号之间不能有空格

  • 命名只能使用英文字母、数字和下划线,首字符不能是数字

  • 不能使用bash中的关键字【使用help查看关键字】

VAR1=1
age=18 整形
name='zipeng' 字符串
score=88.8 浮点

4.1.2 注意事项

  • 字符串要用单引号或双引号引起来
  • 建议变量名为大写,和命令区分

4.1.3 示例

# 定义本地变量
[root@VM-0-6-centos study]# name='zipeng'
[root@VM-0-6-centos study]# age=23
[root@VM-0-6-centos study]# score=98
[root@VM-0-6-centos study]# echo $age
23

# 取消变量 unset
[root@VM-0-6-centos study]# unset age
[root@VM-0-6-centos study]# echo $age


# 有类型变量 declare
-i 将变量看成整数
-r 使变量只读readonly,该变量的值无法改变,并且不能被unset
-x 标记变量通过环境导出export
-a 指定为索引数组【普通数组】,查看普通数组
-A 指定为关联数组,查看关联数组
[root@VM-0-6-centos ~]# declare -i num='asd'
[root@VM-0-6-centos ~]# echo $num
0
[root@VM-0-6-centos ~]# num=100
[root@VM-0-6-centos ~]# echo $num
100
[root@VM-0-6-centos ~]# declare -r num
[root@VM-0-6-centos ~]# num=200
-bash: num: readonly variable
[root@VM-0-6-centos ~]# unset num
-bash: unset: num: cannot unset: readonly variable

[root@VM-0-6-centos ~]# declare -x
declare -x HISTSIZE="3000"
declare -x HISTTIMEFORMAT="%d/%m/%y %T "
declare -x HOME="/root"
declare -x HOSTNAME="VM-0-6-centos"
...

4.2 变量分类

4.2.1 本地变量

用户自定义的变量,定义在脚本或者当前终端中,脚本执行完毕或终端结束,变量失效。

4.2.2 环境变量

定义在用户家目录下的.bashrc或.bash_profile文件中,用户私有变量,只能本用户使用

查看当前用户的环境变量 env

查询当前用户的所有变量【临时变量与环境变量】set

4.2.3 export 将当前变量变成环境变量

方式一:
[root@VM-0-6-centos ~]# export A=hello
[root@VM-0-6-centos ~]# env | grep ^A
A=hello

方式二:
[root@VM-0-6-centos ~]# A=hello
[root@VM-0-6-centos ~]# export A
[root@VM-0-6-centos ~]# env | grep ^A
A=hello

方式三:
[root@VM-0-6-centos ~]# vim .bashrc
export A='hello'
[root@VM-0-6-centos ~]# env | grep ^A
A=hello

关于export的说明:

  1. 用户登录时

    ​ 用户登录到Linux系统后,系统将启动一个用户shell。在这个shell中,可以使用shell命令或声明变量,也可以创建并运行shell脚本程序。

  2. 运行脚本时

    ​ 运行脚本程序时,系统将创建一个子shell。此时,系统中将有两个shell,一个是登录时系统启动的shell,一个是系统为运行脚本程序创建的shell。当一个脚本程序运行完毕,它的脚本shell将终止,可以返回到执行该脚本之前的shell。

    ​ 用户可以有许多shell,每一个shell都是由某个父shell派生的。在子shell中定义的变量只在该子shell内有效。如果一个shell脚本程序定义了一个变量,当该脚本程序运行时,这个定义的变量只能在该shell中使用,其他shell不能使用。可以使用export对已定义的变量进行输出,使其可以在其他shell中被引用。

    export命令将使系统在创建每一个新的shell时定义这个变量的一个拷贝。这个过程称之为变量输出。

image-20211106163710049

4.2.4 全局变量

使用export命令将本地变量输出为当前shell中的环境变量

所有用户及shell都可以使用,可以在/etc/profile /etc/bashrc下永久定义

打印全局变量 printenv

定义格式
export AGE=23

4.2.5 内置变量

系统变量【内置bash中的变量】:shell本身已经固定好了它的名字和作用。

$?: 上一条命令执行后返回的状态,当返回状态为0表示执行正常,非0表示执行异常或出错
	若退出状态值为0,表示命令执行成功
	若退出状态为127,表示command not found
	若退出状态为126,表示找到了该命令但无法执行【权限不够】
	若退出状态为1&2,表示没有那个文件或目录

$$: 当前所在进程的进程号
	kill -9 `echo $$` = exit 【退出当前会话】
	
$!: 当前终端后台运行的最后一个进程号
!$: 调用最后一条命令历史中的参数
!!: 调用最后一条命令历史
$#: 脚本后面接的参数的个数
$*: 脚本后面所有参数,参数当成一个整体输出和,每一个变量参数之间以空格隔开
$@: 脚本后面所有参数,参数是独立的,也是全部输出

$0: 当前执行的进程/程序名
$1~$9: 位置参数变量
${10}~${n}: 扩展位置参数变量  第10个位置及以后变量必须用{}大括号括起来

了解$*$@的区别: $*:表示将变量看成一个整体 $@:表示变量是独立的

示例:

[root@VM-0-6-centos study]# touch 2.sh
[root@VM-0-6-centos study]# vim 2.sh
[root@VM-0-6-centos study]# cat 2.sh
#!/bin/bash

echo "程序名:/$0 = $0"
echo "脚本第一个参数:/$1 = $1"
echo "脚本参数个数:/$# = $#"
echo "脚本所有参数:/$* = $*"
echo "脚本所有参数:/$@ = $@"
echo "脚本第11个参数:/${11} = ${11}"
[root@VM-0-6-centos study]# chmod +x 2.sh
[root@VM-0-6-centos study]# . 2.sh a b c
程序名:/-bash = -bash
脚本第一个参数:/a = a
脚本参数个数:/3 = 3
脚本所有参数:/a b c = a b c
脚本所有参数:/a b c = a b c
脚本第11个参数:/ = 
[root@VM-0-6-centos study]# touch 3.sh
[root@VM-0-6-centos study]# vim 3.sh
[root@VM-0-6-centos study]# cat 3.sh
#!/bin/bash

# DESC: 了解 $* 和 $@ 的区别

for i in "$@"
do
    echo $i
done

echo "====我是分隔线===="

for i in "$*"
do
    echo $i
done
[root@VM-0-6-centos study]# chmod +x 3.sh
[root@VM-0-6-centos study]# . 3.sh a b c
a
b
c
====我是分隔线====
a b c

4.2.6 变量总结

本地变量:当前用户自定义的变量。当前进程中有效,其他进程及当前进程的子进程无效

环境变量:当前进程有效,并且能够被子进程调用

全局变量:全局所有的用户和程序都能调用,且继承,新建的用户也默认能调用

内置变量:shell本身已经固定好了它的名字和作用

变量类型 作用域 生命周期
本地变量 当前shell环境【子shell不能用】 脚本结束或终端结束
环境变量 当前shell或者子shell 当前进程结束
全局变量 所有用户及shell环境 关机
内置变量 所有用户及shell环境 关机

4.2.7 变量取值

读取变量内容符:

[root@VM-0-6-centos study]# name=zipeng
[root@VM-0-6-centos study]# echo $name
zipeng

变量读取过程中,默认单引号是不解释变量的,比如:
[root@VM-0-6-centos study]# echo '$name'
$name
如果必须使用单引号还要读取变量的值,则可以使用eval命令【重新计算求出参数的内容】
[root@VM-0-6-centos study]# eval echo '$name'
zipeng

4.2.8 其他变量【扩展】

  • 取出一个目录下的目录和文件:dirname 和 basename
  • 变量内容的删除和替换:
    • 一个%代表从右往左去掉一个 key
    • 两个%%代表从右往左最大去掉 key
    • 一个#代表从左往右去掉一个 key
    • 两个##代表从左往右最大去掉 key
[root@VM-0-6-centos study]# B=/root/Desktop/shell/name.txt
[root@VM-0-6-centos study]# dirname $B
/root/Desktop/shell
[root@VM-0-6-centos study]# basename $B
name.txt

[root@VM-0-6-centos study]# cat 4.sh
#!/bin/bash

url=www.taobao.com
echo "获取变量的长度:" ${#url}
echo ${url#*}
echo ${url##*.}
echo ${url%.*}
echo ${url%%.*}
[root@VM-0-6-centos study]# . 4.sh
获取变量的长度: 14
www.taobao.com
com
www.taobao
www

5. 实战-数据磁盘初始化

5.1 应用场景

​ 生产环境中的服务器一般会分为系统盘和数据盘。以dell R730为例子,该服务器是一个2U的机架式服务器,满载可以挂载14块磁盘【2块在机箱内做系统盘,12块在面板做数据盘】。

​ 一般策略是系统盘做raid1,为保障系统稳定性,12块数据盘做raid10或者raid50,这样在保障数据盘容错的同时还能做到优化IO的效果。

​ raid磁盘的容量是一定的,线上的数据又是不断增长的,若数据磁盘填满,该怎么办?为了解决这个问题,人们想到LVM【逻辑卷管理系统】。当前数据盘容量不够用的时候,可以通过san存储获得网络磁盘,然后将该网络存储动态加入LVM的卷组后就可以扩大LV了。整个过程采用在线扩容的方式,不会影响线上业务。在系统中把raid数据盘在存数据之前做成LVM磁盘,方便后续的扩容。

有数据的磁盘不能再做LVM,因为需要格式化,数据会全部丢失,必须提前布局。

5.2 场景需求

给虚拟机添加一块磁盘(以sdb为例),要求使用脚本对该磁盘分三个区:
1. 主分区 /dev/sdb3 543M 文件系统 ext4 要求开机自动挂载到 /data/data1 目录
2. 逻辑分区 /dev/sdb5 2G
3. 逻辑分区 /dev/sdb6 3G

使用/dev/sdb5 /dev/sdb6 新建卷组vg100 并创建一个PE为16M,容量为2.5G的逻辑卷lv100
格式化为xfs,默认开机自动挂载到/data/data2目录

5.3 脚本实现思路

1. 分区
2. 创建逻辑卷
	2.1 创建物理卷
	2.2 创建卷组
	2.3 创建逻辑卷
3. 格式化 /dev/sdb3 /dev/vg100/lv100
4. 修改/ect/fstab文件
5. 挂载分区
6. 验证并输出挂载结果

5.4 代码实现

#!/bin/bash

#Author: Li zipeng
#Created Time: 2021/11/7
#Release: 1.0
#Description: 数据磁盘初始化

#给虚拟机添加一块磁盘【以sdb为例】要求使用脚本对该磁盘分三个区
# 1.主分区 /dev/sdb3 543M 文件系统 ext4 要求开机自动挂载
# 2.逻辑分区 /dev/sdb5 2G
# 3.逻辑分区 /dev.sdb6 3G
#使用 /dev/sdb5 /dev/sdb6 新建卷组vg100,并创建一个PE为16M,容量为2.5G的逻辑卷lv100,格式化
#为xfs,默认开机自动挂载到/data/data2目录

#1.分区
fdisk /dev/sdb <<EOF
n
p
3

+543M
n
e
4


n

+2G
n

+3G
w
EOF

#2.创建逻辑卷
#	2.1创建物理卷
	pvcreate /dev/sdb5 /dev/sdb6
#	2.2创建卷组
	vgcreate -s 16M vg100 /dev/sdb{5..6}
#	2.3创建逻辑卷
	lvcreate -L 2.5G -n lv100 vg100
#3.格式化
mkfs.ext4 /dev/sdb3
mkfs.xfs /dev/vg100/lv100

#4.修改/etc/fstab,实现自动挂载
echo "/dev/sdb3 /data/data1 ext4 defaults 0 0" >> /etc/fstab
echo "/dev/vg100/lv100 /data/data2 xfs defaults 0 0" >> /etc/fstab

#5.挂载分区
mkdir -p /data/data{1..2}
mount -a

#6.验证并输出挂载结果
mount | grep "/dev/sdb3"
test $? -eq 0&&echo "/dev/sdb3 挂载成功" || echo "/dev/sdb3 挂载失败"

#注意检索的时候,mount输出中LV的表示方式,或者直接检索挂载点/data/data2也可以
mount | grep "vg100-lv100"
test $? -eq 0&&echo "/dev/vg100/lv100 挂载成功" || echo "/dev/vg100/lv100 挂载失败" 

5.5 验证

使用如下命令查看是否挂载成功
df -Th

6. shell脚本格式化输出

6.1 echo命令

**功能:**将内容输出到默认显示设备

**应用场景:**需要计算机程序输出的地方

语法:echo [-ne][字符串]
补充说明:
1. echo会将输出的字符串送往标准输出
2. 输出的字符串间以空白字符隔开,并在最后加上换行号

options:
-n	不要在最后自动换行
-e	若字符串中出现以下字符,则特别加以处理,不会将它当成一般文字输出:
	\a	发出警告声
	\b	删除前一个字符
	\t	插入tab
	\n	换行且光标移至行首
	\c	最后不加上换行符号
	\f	换行但光标仍旧停留在原来的位置
	\r	光标移至行首,但不换行
	\v\f相同
	\	插入\字
	\0nnn	打印nnn[八进制]所代表的的ASCII字符【备注 \后面接的是数字0而不是字母o】
	\xNN	打印NN[十六进制]所代表的的ASCII字符
	
--help 显示帮助信息
--version 显示版本信息

[root@VM-0-6-centos ~]# echo -e "\0123"
S
[root@VM-0-6-centos ~]# echo -e "\x61"
a

6.2 输出颜色字体

echo显示内容带颜色,需要使用参数-e

格式:
echo -e "\033[字体背景颜色;文字颜色m字符串\033[0m"

例如:

image-20211111143818809

其中,41的位置代表底色,36m的位置是代表字的颜色

  1. 字背景颜色和文字颜色之间是英文的
  2. 文字颜色后面有个m
  3. 字符串前后可以没有空格,如果有的话,输出也是同样有空格
字颜色: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 显示光标
  
用法例子  光标下移三行  
echo -e "\033[0m today is fine \033[3B"
 today is fine 

7. 脚本用户交互

7.1 read命令

**功能:**默认接受键盘的输入,回车符代表输入结束

**应用场景:**人机交互

命令选项:

-p	打印信息
-t	限定时间
-s	不回显
-n	输入字符个数

7.2 交互输入案例

**案例需求:**写一个系统用户交互登录界面脚本,仿linux界面登录

#!/bin/bash

#Author: zipeng
#Release: 1.0
#Description: 仿真登录

IP=`ifconfig eth0 | egrep -w "inet" | awk '{print $2}'`

#1.清屏
clear
#2.输出提示信息
echo "CentOS Linux 8 (Core)"
echo -e "Kernel `uname -r` on an `uname -m\n`"
echo -e "Web console: https://localhost:9090/ or https://$IP:9090/ \n"

#3.交互输入登录名
echo -n "$HOSTNAME login: "
read account

#4.交互输入密码
read -s -t30 -p "Password: " pw
echo

8. shell运算详解

8.1 赋值运算

赋值运算符 =
a=10
name='zipeng'
字符串必须使用引号引起来

8.2 算术运算

四则运算符:+ - * /

取余:%

幂次:**

运算命令:

  • 整型运算
    • expr
    • let
    • $(())
    • bc
  • 浮点运算
    • bc

8.2.1 整形运算

expr命令:只能做整数运算,格式比较古板,注意空格

[root@VM-0-6-centos study]# expr 1 + 2
3
[root@VM-0-6-centos study]# expr 5-6
5-6
[root@VM-0-6-centos study]# expr 5 - 6
-1
[root@VM-0-6-centos study]# expr 5 \* 6
30

let命令:只能做整数运算,且运算元素必须是变量,无法直接对整数做运算

[root@VM-0-6-centos study]# let 5 + 2
-bash: let: +: syntax error: operand expected (error token is "+")
[root@VM-0-6-centos study]# let a=100+2;echo $a
102
[root@VM-0-6-centos study]# let a=100**2;echo $a
10000

双小圆括号运算,在shell中(())也可以用来做数学运算

[root@VM-0-6-centos study]# echo $((100+3))
103
[root@VM-0-6-centos study]# echo $((100-3))
97
[root@VM-0-6-centos study]# echo $((100**3))
1000000
[root@VM-0-6-centos study]# echo $((100%3))
1

8.2.2 浮点运算

浮点运算是采用的命令组合的方式来实现的 echo "scale=N;表达式"|bc

[root@VM-0-6-centos study]# echo "scale=2;100/3"|bc
33.33

8.2.3 实现四则运算器

#!/bin/bash
# 
#Author: zipeng
#Release: 
#Description: 简单计算器

echo "$1 $2 $3"|bc

8.2.4 内存使用率统计

#!/bin/bash
# 
#Author: zipeng
#Release: 
#Description:内存使用率计算脚本

#free
#1、获得内存总量
memory_total=`free -m|grep -i "mem"|tr -s " "|cut -d " " -f2`

#2、获得内存使用的量
memory_use=`free -m|grep -i "mem"|tr -s " "|cut -d " " -f3`

#3、计算输出
#运算的时候是否需要小数点 浮点运算,要考虑使用的命令 (难点 重点)
#echo "内存使用率: $((memory_use*100/memory_total))%"
#难点:浮点运算中,同优先级的情况下,大数除以小数 尽可能保证精确
echo "内存使用率: `echo "scale=2;$memory_use*100/$memory_total"|bc`%"


[root@VM-0-6-centos study]# . 8.sh
内存使用率:69%

8.3 比较运算

​ 计算机除了算术和赋值运算外,还有比较运算,比如说比较两个数的关系,比较两个字符串的关系【用户登2录系统】等。接下来我们学习如何在shell中进行比较运算

整型比较运算

运算符解释:
	精确比较:
		-eq	等于 equal
		-gt	大于
		-lt	小于
	模糊比较:
		-ge	大于或等于
		-le	小于或等于
		-ne	不等于

通过test命令比较两个整数关系

[root@VM-0-6-centos study]# test 100 -gt 300; echo $?
1
[root@VM-0-6-centos study]# test 100 -lt 300; echo $?
0
[root@VM-0-6-centos study]# test 100 -ne 300; echo $?
0

8.3.1 实现对两个整数关系的判断

#!/bin/bash

# 判断两个数的关系
if [ $1 -gt $2 ];then
	echo "$1 > $2"
elif [ $1 -eq $2 ];then
	echo "$1 = $2"
else
	echo "$1 < $2"
fi
[root@VM-0-6-centos study]# vi 9.sh 
[root@VM-0-6-centos study]# . 9.sh 100 200
100 < 200
[root@VM-0-6-centos study]# . 9.sh 300 200
300 > 200
[root@VM-0-6-centos study]# . 9.sh 200 200
200 = 200

8.3.2 判断两个浮点数的关系

默认情况下shell是不能判断浮点的,那么在linux中又避免不了需要进行浮点运算,那怎么解决 解决思路如下: 1)两个数据同时放大到整数倍 2)处理掉小数点位,保留整数位 3)进行整形判断

#!/bin/bash

#Description:判断两位小数点的关系

#1、交互或者外传参的方式获得两个整数
#$1 $2
[ $# -lt 2 ]&&echo "need two args"&&exit 1

#采用外传参的方式接收数据并放大100倍,并处理为整数
num1=`echo "scale=2;$1*100"|bc|cut -d "." -f1`
num2=`echo "scale=2;$2*100"|bc|cut -d "." -f1`

#2、比较运算
if [ $num1 -gt $num2 ];then
   #3、输出结果
   echo "$1 > $2"
elif [ $num1 -lt $num2 ];then
   echo "$1 < $2"
else
   echo "$1 = $2"
fi
[root@VM-0-6-centos study]# . 10.sh 3.14 6.22
3.14 < 6.22

8.4 字符串比较运算

运算符解释,注意字符串一定别忘了使用引号引起来 == 等于
!= 不等于 -n 检查字符串的长度是否大于0
-z 检查字符串的长度是否为0

比较两个字符串关系
[root@VM-0-6-centos ~]# test 'root' == 'root'; echo $?
0
[root@VM-0-6-centos ~]# test 'root' != 'root'; echo $?
1
[root@VM-0-6-centos ~]# name=
[root@VM-0-6-centos ~]# test -n "$name"; echo $?
1
[root@VM-0-6-centos ~]# test -z "$name"; echo $?
0

案例-登录校验

#!/bin/bash
# 
#Author: zipeng
#Created Time: 
#Release: 
#Description: 仿真登陆
####
default_account='root'
default_pw='123456'


######main
#1、清屏
clear
#2、输出提示信息
echo "CentOS Linux 7 (Core)"
echo -e "Kernel `uname -r` on an `uname -m`\n"

#3、交互输入登陆名
echo -n "$HOSTNAME login: "
read account

#4、交互输入密码
read -s -t30 -p "Password: " pw

#5、判断用户输入是否正确
if [ "$default_account" == "$account" ] && [ "$default_pw" == "$pw" ];then
   clear
   echo -e "\nwelcome to root"
else
   echo  "用户名或密码错误..."
   #输入错误,再次调用本脚本
   sh $0
fi

8.5 逻辑运算

逻辑与	&&
逻辑或	||
逻辑非	!

登录案例

#!/bin/bash
echo "CentOS linux 8 (Core)"
echo -e "Kernel `uname -r` on an `uname -m` \n"

echo -n "$HOMENAME login: "
read myuser

read -p "password: " -s -t 5 -n 6 pw
[ $myuser == 'root' ] && [ $pw == '123456' ] && echo yes || echo no

8.6 文件判断

文件类型、权限、新旧判断

8.6.1 test判断命令

检测文件类型和比较运算

test [命令选项] 表达式

options:
-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节点
文件类型判断:
[root@VM-0-6-centos study]# test -f /etc/passwd; echo $?
0
[root@VM-0-6-centos study]# test -f /etc; echo $?
1
[root@VM-0-6-centos study]# test -d /etc; echo $?
0

权限判断
[root@VM-0-6-centos ~]# test -f /etc/passwd; echo $?
0
[root@VM-0-6-centos ~]# test -x /root/anaconda-ks.cfg; echo $?
1

8.6.2 检查进程并杀死

#!/bin/bash

# AUthor: zipeng
# Description: 找到服务的PID号,如果服务开启则杀死,否则提示服务已经关闭或不存在
# Time: 2021/11/11 22:35

# 判断PID
if [ -f /var/run/$1.pid ]; then
	# 如果存在
	PID=`cat /var/run/$1.pid`
	# 统计进程数
	process_num=`ps aux|grep $PID|wc -l`
	# 判断进程数大于2则杀死
	if [ $process_num -ge 2 ]; then
		kill -s QUIT $PID
	else
		# 判断小于2则提示进程不存在,同时删除服务PID文件
		echo "service $1 is close"
		rm -f /var/run/$1.pid
	fi
else
	# 不存在
	echo "service $1 is close"
fi

9. shell数组

9.1 定义

数组名称=(元素1 元素2 元素3 ...)

9.2 赋值方式

  • 一次一赋值
array[0]=v1
array[1]=v2
array[2]=v3
array[3]=v4
  • 一次赋多个值
array=(var1 var2 var3 var4 ...)
array1=(`cat /etc/passwd`) #将文件每一行赋值给array数组
array2=(`ls /root`)
array3=(harry amy jack "Miss zhang")
array4=(1 2 3 4 "hello world" [10]=linux)

9.3 数组取值

取值方式:${数组名称[索引]}

${array[i]} i 表示元素的索引
使用@或*可以获取数组中的所有元素:
echo ${array[0]}
echo ${array[*]}	获取数组里的所有元素
echo ${#array[*]}	获取数组里的所有元素个数
echo ${!array[@]}	获取数组元素的索引
echo ${array[@]:1:2} 访问指定的元素;1代表从索引为1的元素开始获取;2代表获取后面几个元素

9.4 关联数组赋值

  • 一次一赋值
数组名[索引]=变量值
array[linux]=one
array[java]=two
array[php]=three
  • 一次赋值多个
array=([name1]=harry [name2]=jack [name3]=amy [name4]="Miss zhang")
  • 查看关联数组
declare -A
declare -A asso_array1='([php]="three" [java]="two" [linux]="one" )'
declare -A asso_array2='([name3]="amy" [name2]="jack" [name1]="harry" [name4]="Miss zhang" )'

9.5 监控CPU平均负载值的脚本

#!/bin/bash

# Author: zipeng
# Description: 打印cpu 1min 5min 15min的负载值

# 1.收集负载值
cpu_load=(`uptime|tr -s " "|cut -d " " -f9-11|tr "," " "`)

# 2.输出负载值
echo "CPU 1min平均负载为:${cpu_load[0]}"
echo "CPU 5min平均负载为:${cpu_load[1]}"
echo "CPU 15min平均负载为:${cpu_load[2]}"

10. if判断语句

10.1 单if语法

语法格式:
if [ condition ] # condition值为true or false
	then	# 条件为真的时候执行
		commands	# 代码块 一行或者多行代码
fi	# 语句结束
#!/bin/bash

if [ $USER != 'root' ]
    then
        echo "ERROR: need to be root so that"
fi

10.2 if...else语句

语法格式:
if [ condition ]
	then
		commands1
else
	commands2
fi
#!/bin/bash 

if [ $USER == 'root' ]
   then
	echo "hey admin"
else
	echo "hey guest"
fi

10.3 if...elif...else

语法格式:
if [ condition ]
	then
		command
elif [ condition ]
	then
		command
else
	command
fi
#!/bin/bash
#1、条件
#判断内存的使用率
#60以下    ok 绿色  32m
#60以上    黄色警告   33m
#70以上    粉色严重警告 35m 
#80以上    红色警告 31m

#2、算法
#收集内存使用率  $1 
#
#多条件判断
if [ $1 -gt 80 ]
then
	echo -e "\033[31m 警告 \033[0m"
elif [ $1 -gt 70 ]
then
	echo -e "\033[35m 警告 \033[0m"
elif [ $1 -gt 60 ]
then
	echo -e "\033[33m 警告 \033[0m"
else
	echo -e "\033[32m OK \033[0m"
fi

10.4 if嵌套

#!/bin/bash
#Author: 
#Created Time: 
#Script Description: 

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

10.5 Nginx安装初始化脚本

#!/bin/bash
# Author: zipeng
# Time:
# Description: 软件包下载 => 解压 => 安装依赖包 => 安装Nginx => 返回结果

source_pkg=nginx-1.19.2.tar.gz
# 1.软件包下载
wget http://nginx.org/download/$source_pkg
# 2.解压
if [ -f $source_pkg ]; then
	tar xf $source_pkg && cd nginx-1.19.2
else
	echo "not found $source_pkg"
	exit 1
fi
# 3.安装依赖包
if ! ( yum -y install pcre-devel zlib-devel ); then
	echo "yum: install soft error"
	exit 1
fi
# 4.配置
if ./configure 1 > /dev/null; then
	# 判断make执行结果
	if make 1>/dev/null; then
		# 判断make install安装结果
		if make install 1>/dev/null; then
			echo "nginx 安装成功"
		else
			echo "nginx 安装失败"
			exit 1
		fi
else
	echo "configure fail"
	exit 1
fi

10.6 if高级用法

条件符号使用双圆括号,可以在条件中植入数学表达式if(())

#!/bin/bash

if (( (5+5-5)*5/5 > 10 )); then
	echo "yes"
else
	echo "no"
fi

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

#!/bin/bash

for var in ab ac rx bx rvv vt
	do
		if [[ "$var" == r* ]]; then
			echo "$var"
		fi
done

10.7 简写if

省去了关键字,条件为真采用&&符号链接命令块,条件为假采用||链接命令块

简写if一般用在简单的判断中

if [ ! -d /tmp/zipeng ]; then
	mkdir /tmp/zipeng
fi

可以简写为:[ ! -d /tmp/zipeng ] && mkdir /tmp/zipeng

if[ $USER == 'root' ]; then
	echo "hello root"
else
	echo "hello guest"
fi

可以简写为:[ $USER == 'root' ] && echo "hello root" || echo "hello guest"

10.8 训练

1.计算机状态监控

​ 能ping通就算up,不通为down,需要考虑网络延迟问题造成的假报警问题。


2.监控一个服务端口,判断其状态


3.打印内存使用率脚本:打印内存使用率,swap使用率,buff&cache使用量


4.打印磁盘使用率脚本:对本机的磁盘使用率统计并打印结果


5.网卡发送和接收数据量监控,按秒统计


6.URL监控脚本,对某个URL进行监控,返回值为200则代表成功访问


7.mysql binlog备份脚本:每天零点自动备份前一天的binlong日志,打包后发送给备份服务器


8.闰年判断


9.判断/tmp/run目录是否存在,不存在就创建,存在则删除目录所有文件


10.nginx安装脚本


11. if条件语句详解

如何在if条件中植入shell运算条件

11.1 与文件存在与否的判断

-e 是否存在 不管是文件还是目录,只要存在,条件就成了
-f 是否为普通文件
-d 是否为目录
-S 是否为socket
-p 是否为管道
-c character
-b block
-L 软link

11.2 文件权限相关的判断

-r 当前用户对其是否可读
-w 当前用户对其是否可写
-x 当前用户对其是否可执行
-u 是否有suid
-g 是否sgid位
-k 是否有t位

11.3 两个文件的比较判断

file1 -nt file2 比较1是否比2新
file1 -ot file2 旧
file1 -ef file2 是否为同一个文件,或者用于判断硬连接,是否指向同一个inode

11.4 整数之间的判断

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

11.5 字符串之间的判断

-z 是否为空字符串 长度为0就成立
-n 是否为非空字符串 非空就成立
string1 == string2 是否相等
s1 != s2 不等

# 用户登录验证
read -p "user: " myuser
if [ -z "$myuser" ]; then
	echo "用户名为空"
	exit 1
fi

read -p "password: " mypw
if [ -n "$mypw" ]; then
	if [ $myuser == 'root' ]; then
		if [ $mypw == 'abc-123' ]; then
			echo 'welcome root'
		else
			echo '密码错误'
			exit 1
		fi
	else
		echo '用户名错误'
		exit 1
else
	echo '密码不能为空'
	exit 1
fi

11.6 多重条件判断

逻辑判断符号:&& || !

# 用户登录验证
read -p "user: " myuser
if [ -z "$myuser" ]; then
	echo "用户名为空"
	exit 1
fi

read -p "password: " mypw
if [ -n "$mypw" ]; then
	if [ $myuser == 'root' ] && [ $mypw == 'abc-123' ]; then
		echo 'welcome root'
	else
		echo '用户名或密码错误'
		exit 1
	fi
else
	echo '密码不能为空'
	exit 1
fi

11.7 练习

1.判断/tmp/scholl目录,没有则创建


2.判断是否有/usr/bin/wget命令,没有则安装


3.判断当前用户是否为管理员,UID为0或者为root都算管理员


4.打印物理内存使用率


12. 流程控制-for循环语句

12.1 基本语法

列表for循环:用于将一组命令执行已知的次数

for variable_name in {list}
	do 
		command
		command
		...
	done
或者
for variable in a b c
	do
		command
		command
		...
	done

12.2 循环集合来源

来自一个范围

for var in {1...10}
	do
		echo $var
	done

直接赋值

for var in 1 2 3 4 5
	do
		echo $var
	done

赋值来自命令

for var in `seq 10`
	do
		echo $var
	done

12.3 不带列表循环

不带列表的for循环,执行时由用户指定参数和参数的个数

for variable
	do
		command
		command
	done
for car
	do
		echo $var
	done
echo "脚本后面有$#个参数"

12.4 C语言格式语法

for (( expr1;expr2;expr3 ))
	do
		command
		command
	done

for (( i=1;i<=5;i++ ))
	do
		echo $i
	done

多变量用法
for ((A=1,B=10;A<10,B>1;A++,B--))

12.5 训练

写一个扫描软件,扫描本地网络中存活的机器

#!/bin/bash

netsub="192.168.1."
for ip in `seq 1 254`
	do(
		if ping -c 1 $netsub$ip &>/dev/null; then
			echo "$netsub$ip is open"
		else
			echo "$netsub$ip is close"
		fi
	) &
	done

13. 循环控制

13.1 beak语句

终止循环,执行循环体后的代码

#!/bin/bash

for i in `seq 1 9`
	do
		echo $i
		if [ $i -eq 5 ]; then
			break
		fi
	done

echo "执行完毕"

13.2 continue语句

#!/bin/bash

for ((i=1;i<10;i++))
	do
		if [ $i -eq 5 ]; then
			continue
		else
			echo $i
		fi
	done
echo "执行完毕"

13.3 sleep语句

控制循环的节奏,控制循环频率

#!/bin/bash

echo -n "倒计时:"
for i in `seq 9 -1 1`
	do
		echo -n -e "\b$i"
		sleep 1
	done
echo "执行完毕"

13.4 参数控制命令-shift

外部传参到循环时,参数管理命令

#!/bin/bash

# 判断外传参的数量
[ $# -lt 3 ] && echo '请输出至少三个参数:' $0 ' $1 $2 $3 ...' && exit 1
# 将参数的数量赋值给count
count=$#
# 通过shift左移参数输出
# 使位置参数向左移动,默认移动1位,可以使用shift 2 传参要是N的整数倍
for ((i=1;i<=$count;i++))
	do
		echo "参数数量:'$#'"
		echo '当前$1的数值是:'$1''
		shift 1
		sleep 1
	done
echo '执行完毕'

13.5 脚本退出命令-exit

退出程序并释放占用的系统资源

#!/bin/bash

for i in `seq 1 9`
	do
		echo $i
		if [ $i -eq 5 ]; then
			exit 0
		fi
	done
echo "执行完毕"

14. while介绍

14.1 语法

while [ 表达式 ]
	do
		command
	done
while [ 1 -eq 1 ] 或者 (( 1 > 2 ))
	do 
		command
	done
#!/bin/bash

num=1
while [ $num -le 5 ]
	do
		echo $num
		let num++
	done

14.2 判断条件

14.2.1 比较运算

#!/bin/bash

read -p "请输入一个小写字母,按Q退出:" choose
while [ $choose != 'Q' ]
	do
		echo "你输入的是:$choose"
		read -p "请输入一个小写字母,按Q退出" choose
	done

14.2.2 逻辑运算

while [ $money -lt 10000 ] || [ $car -lt 1 ] || [ $house -lt 2 ]
	do
		command
	done

14.2.3 文件类型判断

# 使用循环判断/tmp/xxx目录下的文件,如果不是文件类型的打印字符串"目录"

while [ ! -f /tmp/xxx ]
	do
		echo "目录"
		sleep 1
	done

14.2.4 特殊条件

while语句中可以使用特殊条件来进行循环:

  • 符号":" 条件代表真,适用与无限循环
  • 字符串 “true” 条件代表真,适用与无限循环
  • 字符串 "false"条件代表假
while : 
	do
  		echo haha
   		sleep 1
	done

while true
 	do
   		echo haha
   		sleep 1
	done


false   字符串代表假,在while中不会开始循环

15. until语句

条件为假就进入循环;条件为真就退出循环

15.1 语法

until expression [ 1 -eq 1 ] (( 1 >= 1 ))
	do
		command
		command
	done

15.2 案例

i=6
until [ $i -le 5 ]
	do
		echo $i
		let i++
		[ $i -eq 10 ] && break
	done

16. shell函数

16.1 定义

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

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

函数中return说明:
1.return可以结束一个函数,类似于前面讲的循环控制语句break(结束当前循环,执行循环体后面的代码)
2.return默认返回函数中最后一个命令的退出状态,也可以给定参数值,该参数值的范围是0-256之间。
3.如果没有return命令,函数将返回最后一个Shell的退出值。

16.2 函数调用

16.2.1 当前命令行调用

#!/bin/bash

hello(){
	echo "hello zipeng $1"
	hostname
}

menu(){
	cat << -EOF
	1. mysql
	2. web
	3. app
	4. exit
	EOF
}

source fun1.sh
. fun1.sh
hello 888
menu

16.2.2 定义到用户的环境变量中


16.2.3 脚本中调用

#!/bin/bash
#打印菜单
source ./fun1.sh
menu(){
cat <<-END
	h	显示命令帮助
	f	显示磁盘分区
	d	显示磁盘挂载
	m	查看内存使用
	u	查看系统负载
	q	退出程序
	END
}
menu		//调用函数

17. case语句

17.1 语法

case $var in
pattern 1)
	command
	;;
pattern 2)
	command
	;;
pattern 3)
	command
	;;
*)
	command
	;;
esac

17.2 案例

写一个nginx启动管理脚本,可以实现/etc/init.d/nginx start|stop|restart|status|reload

#!/bin/bash

nginx_install_doc=/usr/local/nginx
proc=nginx
nginxd=$nginx_install_doc/sbin/nginx
pid_file=$nginx_install_doc/logs/nginx.pid

# source function library
if [ -f /etc/init.d/functions ]; then
	. /etc/init.d/functions
else
	echo "not found file /etc/init.d/functions"
	exit
fi

# 假如pid文件存在,那么统计一下nginx进程数量
if [ -f $pid_file ]; then
	nginx_process_id=`cat $pid_file`
	nginx_process_num=`ps aux | grep $nginx_process_id | grep -v "grep" | wc -l`
fi

# function
start(){
	# 如果nginx没有启动,则直接启动;否则提示已经启动
	if [ -f $pid_file ] && [ $nginx_process_num -ge 1 ]; then
		echo "nginx running..."
	else
		# 如果pid文件存在,但是没有进程,说明上一次非法关闭了nginx,
		# 造成pid文件没有自动删除,所以启动nginx之前先删除旧的pid文件
		if [ -f $pid_file ] && [ $nginx_process_num -lt 1 ]; then
			rm -f $pid_file
			action "nginx start" $nginxd
		fi
		action "nginx start" $nginxd
	fi
}

stop(){
	# #判断nginx启动的情况下才会执行关闭,如果没启动直接报错,或者提示用户服务没启动
	if [ -f $pid_file ]&&[ $nginx_process_num -ge 1 ]; then
    	action "nginx stop" killall -s QUIT $proc
    	rm -f $pid_file
	else
    	action "nginx stop" killall -s QUIT $proc 2>/dev/null
	fi
}

restart(){
	stop
 	sleep 1
 	start
}

reload(){
	#重载的目的是让主进程重新加载配置文件,但是前提是服务必须开启
	#这里先判断服务是否开启,开启就执行加载,没有开启直接报加载错误
	if [ -f $pid_file ]&&[ $nginx_process_num -ge 1 ]; then
    	action "nginx reload" killall -s HUP $proc
	else
    	action "nginx reload" killall -s HUP $proc 2>/dev/null
	fi
}

status(){
	if [ -f $pid_file ]&&[ $nginx_process_num -ge 1 ]; then
		echo "nginx running..."
	else
 		echo "nginx stop"
	fi 
}

#callable
case $1 in 
	start) 
		start
		;;
	stop) 
		stop
		;;
	restart) 
		restart
		;;
	reload) 
		reload
		;;
	status) 
		status
		;;
	*) 
        echo "USAGE: $0 start|stop|restart|reload|status"
        ;;
esac

18. 正则表达式

支持正则表达式的程序举例:locate|find|vim|grep|sed|awk

18.1 特殊字符

定位符 说明
^ 锚定开头^a以a开头,默认锚定一个字符
$ 锚定结尾a$ 以a结尾 默认锚定一个字符
# 精确匹配
egrep "^ac$" file

# 模糊匹配
egrep "^a" file

egrep "c$" file

18.2 匹配符

匹配符 说明
. 匹配除回车以外的任意一个字符
() 字符串分组
[] 定义字符类,匹配括号中的一个字符
[^] 表示否定括号中出现字符类中的字符,取反
\ 转义字符
|
 1) 精确匹配 a开头c结尾 中间任意 长度为三字符串
 egrep "^a.c$" file
 2)模糊匹配  以cc结尾的字符串   因为$只能锚定单个字符,如果是一个字符串就需要用()来做定义
 egrep "(cc)$" file 
 3)精确匹配  以a开头c结尾  中间是a-z,0-9  长度为三个字节的字符串
 egrep "^a[a-z0-9]c$" file 
 4)精确匹配  以a开头c结尾  中间不包含a-z,0-9  长度为三个字节的字符串
 egrep "^a[^a-z0-9]c$" file 
 5)精确匹配  以e开头f结尾  中间是*号  长度为三个字节的字符串  e*f
 egrep "^e\\*f$" file 
 6)精确匹配 以a开头b或c结尾  中间是任意  长度为三个字节的字符串
 egrep "^a.(b|c)$" file 

18.3 限定符

限定符 说明
* 某个字符之后加星号表示该字符不出现或出现多次
表示该字符出现一次或不出现
+ 表示其前面字符出现一次或多次,但必须出现一次
{n,m} 某个字符之后出现,表示该字符最少n次,最多m次
{m} 正好出现了m次
1. 精确匹配	以a开头 c结尾 中间是有b或者没有b 长度不限的字符串
egrep "^ab*c$" file 
2. 精确匹配 以a开头 c结尾 中间只出现一次b或者没有b的字符串
egrep "^ab?c$" file 
3. 精确匹配 以a开头 c结尾 中间是有b且至少出现一次 长度不限的字符串
egrep "^ab+c$" file
4. 精确匹配 以a开头 c结尾 中间是有b且至少出现两次最多出现四次 长度不限的字符串
egrep "^ab{2,4}c$" file 
5. 精确匹配 以a开头 c结尾 中间是有b且正好出现三次的字符串
egrep "^ab{3}c$" file 
6. 精确匹配 以a开头 c结尾 中间是有b且至少出现一次的字符串
egrep "^ab{1,}c$" file 

18.4 POSIX字符

一次只匹配一个范围中的一个字节

特殊字符 说明
[:alnum:] 匹配任意字母字符0-9 a-z A-Z
[:alpha:] 匹配任意字母,大写或小写
[:digit:] 数字 0-9
[:graph:] 非空字符( 非空格控制字符)
[:lower:] 小写字符a-z
[:upper:] 大写字符A-Z
[:cntrl:] 控制字符
[:print:] 非空字符( 包括空格)
[:punct:] 标点符号
[:blank:] 空格和TAB字符
[:xdigit:] 16 进制数字
[:space:] 所有空白字符( 新行、空格、制表符)
注意[[ ]]  双中括号的意思:  第一个中括号是匹配符[] 匹配中括号中的任意一个字符,第二个[]是格式 如[:digit:]

1. 精确匹配  以a开头c结尾  中间a-zA-Z0-9任意字符  长度为三个字节的字符串
egrep "^a[[:alnum:]]c$" file

19. 文件操作-sed命令

19.1 介绍

​ sed是linux中提供的一个外部命令,它是一个行(流)编辑器,非交互式的对文件内容进行增删改查的操作,使用者只能在命令行输入编辑命令、指定文件名,然后在屏幕上查看输出。它和文本编辑器有本质的区别。

文本编辑器: 编辑对象是文件

行编辑器:编辑对象是文件中的行

sed数据处理原理

image-20211120003317578

19.2 语法

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

command:
	a   在匹配后面添加
	i   在匹配前面添加
	d   删除
	s   查找替换  字符串
	c   更改
	y   转换   N D P 
	p   打印

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

19.3 追加数据操作

演示文档

cat data1

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 'a\append data "haha"' data1

1 the quick brown fox jumps over the lazy dog.
append data "haha"
2 the quick brown fox jumps over the lazy dog.
append data "haha"
3 the quick brown fox jumps over the lazy dog.
append data "haha"
4 the quick brown fox jumps over the lazy dog.
append data "haha"
5 the quick brown fox jumps over the lazy dog.
append data "haha"

在第二行后新开一行追加数据

sed '2a\append data "haha"' data1

1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
append data "haha"
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 '2,4a\append data "haha"' data1

1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
append data "haha"
3 the quick brown fox jumps over the lazy dog.
append data "haha"
4 the quick brown fox jumps over the lazy dog.
append data "haha"
5 the quick brown fox jumps over the lazy dog.

匹配字符串追加【语法:/要匹配的字符串/】

sed '/3 the/a\append data "haha"' data1

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.
append data "haha"
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

19.4 插入数据操作

在data1的每行前插入一行新数据内容: insert data "haha"

sed 'i\insert data "haha"' data1

insert data "haha"
1 the quick brown fox jumps over the lazy dog.
insert data "haha"
2 the quick brown fox jumps over the lazy dog.
insert data "haha"
3 the quick brown fox jumps over the lazy dog.
insert data "haha"
4 the quick brown fox jumps over the lazy dog.
insert data "haha"
5 the quick brown fox jumps over the lazy dog.

在第二行前新开一行插入数据: insert data "haha"

sed '2i\insert data "haha"' data1

1 the quick brown fox jumps over the lazy dog.
insert data "haha"
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.

在第二到四行每行前新开一行插入数据: insert data "haha"

sed '2,4i\insert data "haha"' data1

1 the quick brown fox jumps over the lazy dog.
insert data "haha"
2 the quick brown fox jumps over the lazy dog.
insert data "haha"
3 the quick brown fox jumps over the lazy dog.
insert data "haha"
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

匹配字符串插入: 找到包含"3 the"的行,在其前新开一行插入内容: insert data "haha"

sed '/3 the/i\insert data "haha"' data1

1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
insert data "haha"
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.

19.5 替换数据操作

从标准输出流中做替换,将test替换为text

echo "this is a test" | sed 's/test/text'

this is a text

将data1中每行的dog替换为cat

sed 's/dog/cat/' data1

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.

将data1中第二行的dog替换为cat

sed '2s/dog/cat/' data1

1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy cat.
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.

将data1中第二到第四行的dog替换为cat

sed '2,4s/dog/cat/' data1

1 the quick brown fox jumps over the lazy dog.
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 dog.

匹配字符串替换:将包含字符串"3 the"的行中的dog替换为cat

sed '/3 the/s/dog/cat/' data1

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 cat.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

19.6 更改数据操作

将data1文件中的所有行的内容更改为: change data "data"

sed 'c\change data "haha"' data1

change data "haha"
change data "haha"
change data "haha"
change data "haha"
change data "haha"

将data1文件第二行的内容更改为: change data "haha"

sed '2c\change data "haha"' data1

1 the quick brown fox jumps over the lazy dog.
change data "haha"
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.

将data1文件中的第二、三、四行的内容更改为:change data "haha"

sed '2,4c\change data "haha"' data1

1 the quick brown fox jumps over the lazy dog.
change data "haha"
5 the quick brown fox jumps over the lazy dog.

将data1文件中包含"3 the"的行内容更改为: change data "haha"

sed '/3 the/c\change data "data"' data1

1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
change data "data"
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

19.7 字符转换操作

将data1中的a b c字符转换为对应的 A B C字符

sed 'y/abc/ABC/' data1

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.

19.8 删除数据操作

删除文件data1中的所有数据

sed 'd' data1

删除文件data1中的第三行数据

sed '3d' data1

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.

删除文件data1第三到第四行的数据

sed '3,4d' data1

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

删除文件data1中包含字符串"3 the"的行

sed '/3 the/d' data1

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.

19.9 查看数据操作

打印data1文件内容

sed 'p' data1
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.

打印data1文件第三行的内容

sed '3p' data1
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.
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.

打印data1文件第二、三、四行内容

sed '2,4p' data1
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.

打印data1文件包含字符串"3 the"的行

sed '/3 the/p' data1
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.
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. 

可以看得出,打印内容是重复的行,原因是打印了指定文件内容一次,又将读入缓存的所有数据打印了一次,所以会看到这样的效果, 如果不想看到这样的结果,可以加命令选项-n抑制内存输出即可。

19.10 命令选项说明

使用多个命令 -e, 将brown替换为green dog替换为cat

sed -e 's/brown/green/;s/dog/cat/' data1
1 the quick green fox jumps over the lazy cat.
2 the quick green fox jumps over the lazy cat.
3 the quick green fox jumps over the lazy cat.
4 the quick green fox jumps over the lazy cat.
5 the quick green fox jumps over the lazy cat.

从文件读取编辑器命令 -f 适用于日常重复执行的场景

1)将命令写入文件
vim abc

s/brown/green/
s/dog/cat/
s/fox/elephant/

2)使用-f命令选项调用命令文件
sed -f abc data1 

1 the quick green elephant jumps over the lazy cat.
2 the quick green elephant jumps over the lazy cat.
3 the quick green elephant jumps over the lazy cat.
4 the quick green elephant jumps over the lazy cat.
5 the quick green elephant jumps over the lazy cat.

抑制内存输出 -n

打印data1文件的第二行到最后一行内容  $最后的意思
sed -n '2,$p' data1 

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.

使用正则表达式 -r

打印data1中以字符串"3 the"开头的行内容
sed -n  -r '/^(3 the)/p' data1

3 the quick brown fox jumps over the lazy dog.
  • 数据处理只是在缓存中完成的,并没有实际修改文件内容,如果需要修改文件内容可以直接使用-i命令选项
  • -i是一个不可逆的操作,一旦修改,如果想复原就很困难,几乎不可能
  • -i命令选项提供了备份功能,比如参数使用-i.bak,那么在修改源文件的同时会先备份一个以.bak结尾的源文件,然后再进行修改操作

19.11 标志

在sed命令中,标志是对sed中的内部命令做补充说明

演示文档
cat data2

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
sed 's/dog/cat/2' data2

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标志

​ 将一行中的所有符合的字符串全部执行替换

# 将data1文件中的所有dog替换为cat
sed 's/dog/cat/g' data2

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

p标志

​ 打印文本内容,类似于-p命令选项

sed  '3s/dog/cat/p' data2

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 filename标志

​ 将修改的内容存入filename文件中

# 将修改的第三行内容存储在text文件中
sed  '3s/dog/cat/w text' data2

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
4 the quick brown fox jumps over the lazy dog . dog
5 the quick brown fox jumps over the lazy dog . dog

19.12 搭建ftp服务的脚本

案例需求:

  • 不支持本地用户登录 local_enable=NO
  • 匿名用户可以上传 新建 删除 anon_upload_enable=YES anon_mkdir_write_enable=YES
  • 匿名用户限速500KBps anon_max_rate=500000
#!/bin/bash

ipaddr=`ifconfig eth0 | sed -n '2p' | sed -e 's/.*inet addr:\(.*\) Bcast.*/\1/g'`
iptail=`echo $ipaddr | cut -d '.' -f 4`
ipremote=192.168.1.10

# 修改主机名
hostname server$iptail.zipeng.com
sed -i "/HOSTNAME/cHOSTNAME=server$iptail.zipeng.com" /etc/sysconfig/network
echo "$ipaddr server$iptail.zipeng.cc" >> /etc/hosts

# 关闭防火墙和selinux
service iptables stop
setnforce 0 > /dev/null 2 > &1
sed -i '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config

# test network
ping -c 1 $ipremote > /dev/null 2>&1
if [ $? -ne 0 ];then
	echo "你的网络不通,请先检查你的网络"
	exit 1
else
	echo "网络ok."
fi

# 配置yum源(一般是内网源)
cat > /etc/yum.repos.d/server.repo << end
[server]
name=server
baseurl=ftp://$ipremote
enabled=1
gpgcheck=0
end

# 安装软件
read -p "请输入需要安装的软件,多个用空格隔开:" soft
yum -y install $soft &>/dev/null

# 备份配置文件
conf=/etc/vsftpd/vsftpd.conf
\cp $conf $conf.default

# 根据需求修改配置文件
sed -ir '/^#|^$/d' $conf
sed -i '/local_enable/c\local_enable=NO' $conf
sed -i '$a anon_upload_enable=YES' $conf
sed -i '$a anon_mkdir_write_enable=YES' $conf
sed -i '$a anon_other_write_enable=YES' $conf
sed -i '$a anon_max_rate=512000' $conf

# 启动服务
service vsftpd restart &>/dev/null && echo"vsftpd服务启动成功"

# 测试验证
chmod 777 /var/ftp/pub
cp /etc/hosts /var/ftp/pub

# 测试下载
cd /tmp
lftp $ipaddr <<end
cd pub
get hosts
exit
end

if [ -f /tmp/hosts ];then
	echo "匿名用户下载成功"
	rm -f /tmp/hosts
else
	echo "匿名用户下载失败"
fi

# 测试上传、创建目录、删除目录等
cd /tmp
lftp $ipaddr << end
cd pub
mkdir test1
mkdir test2
put /etc/group
rmdir test2
exit
end

if [ -d /var/ftp/pub/test1 ];then
    echo "创建目录成功"
	if [ ! -d /var/ftp/pub/test2 ];then
    	echo "文件删除成功"
        fi
else
	if [ -f /var/ftp/pub/group ];then
	echo "文件上传成功"
        else
        echo "上传、创建目录删除目录部ok"
        fi 
fi   
[ -f /var/ftp/pub/group ] && echo "上传文件成功"

20. 数据筛选与处理-awk

之前的脚本中我们都是通过grep、cut、tr、uniq、sort等命令通过管道组合在一起将字符串检索出来,然后在通过shell中对应的运算得到结果,在数据检索过程中大家可能也体会到了其中的辛苦和蹩脚

解决办法

一个更加厉害的命令awk。他可以让大家从输出流中检索出自己需要的数据而不需要再向以前那样通过大量命令组合来完成,只需一个命令awk就能完成。并且还能够通过awk对数据进行处理,而不再需要额外的shell运算

20.1 awk介绍

​ awk是一种可以处理数据、产生格式化报表的语言,功能十分强大。awk 认为文件中的每一行是一条记录 记录与记录的分隔符为换行符,每一列是一个字段 字段与字段的分隔符默认是一个或多个空格或tab制表符.

​ awk的工作方式是读取数据,将每一行数据视为一条记录(record)每条记录以字段分隔符分成若干字段,然后输出各个字段的值.

20.2 awk语法

awk [options] '[BEGIN]{program}[END]' [FILENAME]

常用命令选项
-F fs 指定描绘一行中数据字段的文件分隔符  默认为空格
-f file 指定读取程序的文件名
-v var=value 定义awk程序中使用的变量和默认值

注意:awk 程序由左大括号和右大括号定义。 程序命令必须放置在两个大括号之间。由于awk命令行假定程序是单文本字符串,所以必须将程序包括在单引号内。
1)程序必须放在大括号内
2)程序必须要用单引号引起来

awk程序运行优先级是:
    1)BEGIN: 在开始处理数据流之前执行,可选项
    2)program: 如何处理数据流,必选项
    3)END: 处理完数据流后执行,可选项

20.3 awk应用

演示用例

cat test 

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

20.3.1 对字段的提取

字段提取:提取一个文本中的一列数据并打印输出

字段相关内置变量

$0 表示整行文本

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

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

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

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

# 读入test每行数据并把每行数据打印出来
awk '{print $0}' test 

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
# 打印test第六个字段
awk '{print $6}' test
jumps
jumps
jumps
jumps
jumps
# 打印test最后一个字段
awk '{print $NF}' test
dog
dog
dog
dog
dog

20.3.2 命令选项详解

-F: 指定字段与字段的分隔符

当输出的数据流字段格式不是awk默认的字段格式时,我们可以使用-F命令选项来重新定义数据流字段分隔符。比如:

处理的文件是/etc/passwd,希望打印第一列、第三列、最后一列

# 可以看的出,awk输出字段默认的分隔符也是空格
awk -F ':' '{print $1,$3,$NF}' /etc/passwd

root 0 /bin/bash
bin 1 /sbin/nologin
daemon 2 /sbin/nologin
adm 3 /sbin/nologin
lp 4 /sbin/nologin
sync 5 /bin/sync
shutdown 6 /sbin/shutdown
halt 7 /sbin/halt
mail 8 /sbin/nologin
operator 11 /sbin/nologin
games 12 /sbin/nologin
ftp 14 /sbin/nologin
nobody 99 /sbin/nologin
systemd-network 192 /sbin/nologin
dbus 81 /sbin/nologin
polkitd 999 /sbin/nologin
postfix 89 /sbin/nologin
chrony 998 /sbin/nologin
sshd 74 /sbin/nologin
ntp 38 /sbin/nologin
tcpdump 72 /sbin/nologin
nscd 28 /sbin/nologin
mysql 997 /sbin/nologin
www 996 /sbin/nologin
apache 48 /sbin/nologin
tss 59 /sbin/nologin
zabbix 995 /sbin/nologin
saslauth 994 /sbin/nologin
grafana 993 /sbin/nologin

-f file: 如果awk命令是日常重复工作,而又没有太多变化,可以将程序写入文件,每次使用-f调用程序文件就好,方便,高效。

cat abc 
{print $1,$3,$NF}

awk -f abc test
1 quick dog
2 quick dog
3 quick dog
4 quick dog
5 quick dog

-v 定义变量,既然作者写awk的时候就是按着语言去写的,那么语言中最重要的要素—变量肯定不能缺席,所以可以使用-v命令选项定义变量

awk -v name='zipeng' 'BEGIN{print name}'
zipeng

定义了一个变量 name=zipeng,然后调用变量读出数据。

20.3.3 对记录行的提取

记录提取:提取一个文本中的一行并打印输出

记录的提取方法有两种:a、通过行号 b、通过正则匹配

记录相关内置变量

NR: 指定行号 number row

# 提取test第三行数据
# 指定行号为3
awk 'NR==3{print $0}' test 
3 the quick brown fox         jumps over the lazy cat . dog

# 指定行的第一个字段精确匹配字符串为3
awk '$1=="3"{print $0}' test 
3 the quick brown fox         jumps over the lazy cat . dog

20.3.4 对字符串的提取

记录和字段的汇合点就是字符串

# 打印test第三行的第六个字段
awk 'NR==3{print $6}' test
jumps

20.4 awk程序的优先级

关于awk程序的执行优先级,BEGIN是优先级最高的代码块,是在执行PROGRAM之前执行的,不需要提供数据源,因为不涉及到任何数据的处理,也不依赖与PROGRAM代码块;PROGRAM是对数据流干什么,是必选代码块,也是默认代码块。所以在执行时必须提供数据源;END是处理完数据流后的操作,如果需要执行END代码块,就必须需要PROGRAM的支持,单个无法执行。

EGIN:处理数据源之前干什么 不需要数据源就可以执行

PROGRAM: 对数据源干什么 【默认必须有】 需要数据源

END:处理完数据源后干什么 需要program 需要数据源

# 优先级展示
awk 'BEGIN{print "hello ziepng"}{print $0}END{print "bye zipeng"}' test
hello zipeng
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
bye zipeng
# 不需要数据源,可以直接执行
awk 'BEGIN{print "hello world"}'
hello world
# 没有提供数据流,所以无法执行成功
awk '{print "hello world"}'
awk 'END{print "hello world"}'

20.5 awk高级应用

awk是一门语言,那么就会符合语言的特性,除了可以定义变量外,还可以定义数组,还可以进行运算,流程控制,我们接下来看看吧。

20.5.1 awk定义变量和数组

定义变量

awk -v name='zipeng' 'BEGIN{print name}'
zipeng

awk 'BEGIN{name="zipeng";print name}'
zipeng

数组定义方式:数组名[索引]=值

# 定义数组array,有两个元素,分别是100,200,打印数组元素。
awk 'BEGIN{array[0]=100;array[1]=200;print array[0],array[1]}'
100 200
awk 'BEGIN{a[0]=100;a[1]=200;print a[0]}'
100
awk 'BEGIN{a[0]=100;a[1]=200;print a[1]}'
200

20.5.2 awk运算

  1. 赋值运算 =
  2. 比较运算 > >= == < <= !=
  3. 数学运算 + - * / % ** ++ –
  4. 逻辑运算 && || !
  5. 匹配运算 ~ !~ 精确匹配 == !=

赋值运算

awk -v name='zipeng' 'BEGIN{print name}'
zipeng
awk 'BEGIN{school="zipeng";print school}'
zipeng
awk 'BEGIN{array[0]=100;print array[0]}'
100

比较运算:如果比较的是字符串则按ascii编码顺序表比较。如果结果返回为真则用1表示,如果返回为假则用0表示

awk 'BEGIN{print "a" >= "b"}'
0
awk 'BEGIN{print "a" <= "b"}'
1
awk '$1>4{print $0}' test
5 the quick brown fox jumps over the lazy cat . dog
awk 'BEGIN{print 100 >= 1}'
1

数学运算

awk 'BEGIN{print 100+3}'
103
awk -v 'count=0' 'BEGIN{count++;print count}'
1

逻辑运算

awk 'BEGIN{print 100 >= 2 && 100 >= 3}'
1

匹配运算

awk -F ':' '$1 ~ "^ro" {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash

20.5.3 awk环境变量

变量 描述
FIELDWIDTHS 以空格分隔的数字列表,用空格定义每一个数据字段的精确宽度
FS 输入字段分隔符 数据源的字段分隔符 -F
OFS 输出字段分隔符
RS 输入记录分隔符
ORS 输出记录分隔符
# FIELDWIDTHS:重定义列宽并打印,注意不可以使用$0打印所有,因为$0是打印本行全内容,不会打印你定义的字段
awk 'BEGIN{FIELDWIDTHS="5 2 8"}NR==1{print $1,$2,$3}' /etc/passwd
root: x: 0:0:root
# FS:指定数据源中字段分隔符,类似命令选项-F
awk 'BEGIN{FS=":"}NR==1{print $1,$3,$NF}' /etc/passwd
root 0 /bin/bash
# OFS:指定输出到屏幕后字段的分隔符
awk 'BEGIN{FS=":";OFS="-"}NR==1{print $1,$3,$NF}' /etc/passwd
root-0-/bin/bash
# RS:指定记录的分隔符
awk 'BEGIN{RS=""}{print $1,$13,$25,$37,$49}' test
1 2 3 4 5

# 将记录的分隔符修改为空行后,所有的行会变成一行,所以所有字段就在一行了。

# ORS:输出到屏幕后记录的分隔符,默认为回车
awk 'BEGIN{RS="";ORS="*"}{print $1,$13,$25,$37,$49}' test
1 2 3 4 5*[root@zutuanxue ~]# 

可以看出,提示符和输出在一行了,因为默认回车换成了*

20.5.4 流程控制

20.5.4.1 if判断语句
# 演示用例
cat num
1
2
3
4
5
6
7
8
9

# 单if语句,打印$1大于5的行
awk '{if($1>5)print $0}' num
6
7
8
9

# if...else语句,假如$1大于5则除以2输出,否则乘以2输出
awk '{if($1>5)print $1/2;else print $1*2}' num
2
4
6
8
10
3
3.5
4
4.5
20.5.4.2 for循环语句
# 演示用例
cat num2
60 50 100
150 30 10
70 100 40

# 将一行中的数据都加起来  $1+$2+$3
awk '{sum=0;for (i=1;i<4;i++){sum+=$i}print sum}' num2
210
190
210

awk '{
> sum=0
> for (i=1;i<4;i++) {
>     sum+=$i
> }
> print sum
> }' num2
210
190
210
20.5.4.3 while循环语句
# 学习用例
cat num2
60 50 100
150 30 10
70 100 40

# 将文件中的每行的数值累加,和大于或等于150就停止累加
awk '{sum=0;i=1;while(sum<150){sum+=$i;i++}print sum}' num2
210
150
170

awk '{                                                 
sum=0
i=1
while (sum<150) {
   sum+=$i
   i++
}
print sum
}' num2
210
150
170
20.5.4.4 do...while循环
# 学习用例
cat num2
60 50 100
150 30 10
70 100 40

# 将文件中的每行的数值累加,和大于或等于150就停止累加
awk '{sum=0;i=1;do{sum+=$i;i++}while(sum<150);print sum}' num2
210
150
170

awk '{
> sum=0
> i=1
> do {
> sum+=$i
> i++
> }while (sum<150)
> print sum
> }' num2
210
150
170
20.5.4.5 循环控制语句

break

# 学习用例
cat num2
60 50 100
150 30 10
70 100 40

# 累加每行数值,和大于150停止累加
awk '{
> sum=0
> i=1
> while (i<4){
>    sum+=$i
>    if (sum>150){
>       break
>    }
>    i++
> }
> print sum
> }' num2
210
180
170
1
https://gitee.com/alizipeng/the-way-of-programming.git
git@gitee.com:alizipeng/the-way-of-programming.git
alizipeng
the-way-of-programming
编程之路
master

搜索帮助