shell脚本学习

文章目录


一、文本处理工具

1.grep文本处理工具

grep是行过滤工具,用于根据关键字进行行过滤

语法和选项

语法:

# grep [选项] '关键字' 文件名

常用选项:

OPTIONS:
		-i:不区分大小写
		-v:查找不包含指定的内容的行,反向选择
		-w:按单词搜索
		-o:打印匹配到的次数
		-c:统计匹配到的次数
		-n:显示行号
		-r:逐层遍历目录查找
		-A:显示匹配行及后面多少行
		-B:显示匹配行及前面多少行
		-C:显示匹配行前后多少行
		-l:只列出匹配的文件名
		-L:列出不匹配的文件名
		-e:使用正则匹配
		-E:使用扩展正则匹配
		^key:以关键字开头
		key$:以什么关键字结尾
		^$:匹配空行
		--color=auto:可以将找到的关键词部分加上颜色的显示

颜色设置(别名设置):

临时设置:
alias grep='grep --color=auto'
永久设置:
修改配置文件/etc/bashrc,在最后面添加alias grep='grep --color=auto',最后刷新,source /etc/bashrc

2、cut工具

cut是列截取工具,由于列的截取

语法和选项

语法:

# cut 选项 文件名

常用选项:

-c:以字符为单位进行分割,截取
-d:自定义分隔符,分隔符\t
-f:与-d一起使用,指定截取哪个区域
-c:以字符为单位进行分割,截取
实例:
[root@localhost hua]# cut -c1-5 passwd |head 
root:
bin:x
daemo
adm:x
lp:x:
sync:
shutd
halt:
mail:
opera

-d:自定义分隔符,默认为制表符\t
-f:与-d一起使用,指定截取哪个区域
实例:
[root@localhost hua]# cut -d: -f1,7 passwd |head
root:/bin/bash
bin:/sbin/nologin
daemon:/sbin/nologin
adm:/sbin/nologin
lp:/sbin/nologin
sync:/bin/sync
shutdown:/sbin/shutdown
halt:/sbin/halt
mail:/sbin/nologin
operator:/sbin/nologin

3、sort工具

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

语法和选项

-u:去除重复行
-r:降序排列,默认是升序
-o:将排序结果输出到文件中,类似重定向符号>
-n:以数字排序,默认是按字符排序
-t:分隔符
-k:第N列
-b:忽略前导空格
-R:随机排序,每次运行的结果均不同
范例:
#1、先把passwd前面10行重定向到1.txt
#2、以数字降序排序,指定分隔符,第三列,重定向到2.txt
sort -nr -t: -k3 1.txt -o 2.txt  

4、uniq工具

uniq用于去除连续重复

选项:

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

去除重复行sort -u和uniq的区别:

  1. sort -u是去除了所有的重复行
  2. uniq只会去除连续重复的行,不连续的不会去除
uniq和sort结合使用的练习
  1. 对文本重复行进行统计个数
# 先排序,去掉相邻的重复行,然后统计重复行个数
sort 1.txt |uniq -c
  1. 对文本重复行次数进行排序
# sort -n可以识别每行开头的数字,并按其大小进行排序,降序加个r
sort 1.txt |uniq -c|sort -nr
  1. 对重复行去重
sort phonenum.txt |uniq -c |sort -nr |awk '{print $2}'>p.txt		#去重后保留

5、tee工具

tee工具时从标准输入读取并写入到标准输出和文件,即:双向覆盖重定向(屏幕输出|文本输入)

# 取出除了#开头的行内容,和取出除掉空行的行,然后输入到一个文件,相当于备份修改的内容
[root@localhost hua]# grep -v '^#' 1.txt | grep -v '^$' |tee -a beifen.txt
3 4 5
7 8 9
9 7 6

6、diff工具

diff工具用于逐行比较文件的不同

注意:diff描述两个文件不同的方式是告诉我们怎么改变第一个文件之后与第二个文件匹配

语法和选项:

语法:

diff [选项] 文件1 文件2

选项:

选项含义备注
-b不检查空格
-B不检查空白行
-i不检查大小写
-w忽略所有的空格
–normal正常格式显示(默认)
-c上下问格式显示
-u合并格式显示
1、正常显示
# 1、显示结果是1.txt怎么样改变才可以和2.txt相同
# 2、
[root@localhost hua]# cat -n 1.txt 
     1	aaaa
     2	111
     3	hello world
     4	222
     5	333
     6	bbb
[root@localhost hua]# cat -n 2.txt 
     1	aaa
     2	hello
     3	111
     4	222
     5	bbb
     6	333
[root@localhost hua]# diff 1.txt 2.txt 
1c1,2					第一个文件的第1行需要改变(c=change)才能和第二个文件的第1到2行匹配
< aaaa					小于号“<”表示左边文件(file1)文件内容
---						表示分隔符
> aaa					大于号“>”表示右边文件(file2)文件内容
> hello					
3d3						第一个文件的第3行删除(d=delete)后才能和第二个文件的第3行匹配
< hello world
5d4						第一个文件的第5行删除后才能和第二个文件的第4行匹配
< 333
6a6						第一个文件的第6行(a=add)内容后才能和第二个文件的第6行匹配
> 333					需要增减的内容在第二个文件的333
2、上下文显示-c
[root@localhost hua]# diff -c 1.txt 2.txt 
*** 1.txt	2021-04-21 00:28:21.999376943 +0800
--- 2.txt	2021-04-21 00:28:43.802577665 +0800
***************
*** 1,6 ****		以***开头表示file1文件,1,6表示1到6行
! aaaa				!表示该行需要修改彩玉第二个文件匹配
  111				
- hello world		-表示需要删除改行才与第二个文件匹配
  222
- 333				-表示需要删除改行才与第二个文件匹配
  bbb
--- 1,6 ----		以---开头表示file2文件,1,6表示1到6行
! aaa				!表示第一个文件需要修改才与第二个文件匹配
! hello				!表示第一个文件需要修改才与第二个文件匹配
  111
  222
  bbb
+ 333				+表示第一个文件需要加上该行才与第二个文件匹配
3、合并显示-u
[root@localhost hua]# cat 1.txt 
aaaa
111
hello world
222
333
bbb
[root@localhost hua]# cat 2.txt 
aaa
hello
111
222
bbb
333
[root@localhost hua]# diff -u 1.txt 2.txt 
前两行主要列出需要比较的文件名和文件的时间戳,文件名前面的符号----表示file1,+++表示file2
--- 1.txt	2021-04-21 00:28:21.999376943 +0800
+++ 2.txt	2021-04-21 00:28:43.802577665 +0800
@@ -1,6 +1,6 @@		-代表第一个文件的1到6行,+代表第二个文件的1到6行
-aaaa				第一个文件删除aaaa		
+aaa				第一个文件加aaa
+hello
 111
-hello world		第一个文件减去hello world
 222
-333
 bbb
+333
4、diff其他方法使用

1、比较两个目录和目录里的文件的不同

默认情况下也会比较两个目录里相同文件的内容,可以比较内容
如果只需要比较两个目录文件的不同,不需要进一步比较文件内容,需要加-q选项

[root@localhost hua]# mkdir dir1 dir2				#创建两个文件夹
[root@localhost hua]# touch dir1/file{1..5}			#给dir1文件夹创建5个文件
[root@localhost dir2]# touch file{1..3}				
[root@localhost dir2]# touch test{1..2}
[root@localhost hua]# ls -R dir*					#查看两个文件夹下面的文件
dir1:
file1  file2  file3  file4  file5

dir2:
file1  file2  file3  test1  test2

2、将不同的内容打补丁,使其与之相同

(1)#先找出文件的不同,然后输出到一个文件
#-u:上下文模式
#-N:将不存在的文件当做空文件
(2)将不同的内容打补丁到文件
#由于是已file2为准,所以要把不同的打到file1这样才能是file1和file2相同
patch file1 file.patch 


[root@localhost dir1]# cat file1
111
222
333
444
555
666
777
888
[root@localhost dir1]# cat file2
111
222
333
444
[root@localhost dir1]# diff file1 file2
5,8d4
< 555
< 666
< 777
< 888
[root@localhost dir1]# diff file1 file2>file.patch
[root@localhost dir1]# patch file1 file.patch 
patching file file1
[root@localhost dir1]# diff file1 file2
[root@localhost dir1]# cat file1
111
222
333
444

7、paste工具

paste工具用户合并文本行

常用选项:
-d:自定义间隔符,默认是tab,显示结果的间隔用什么表示
-s:串行处理,非并行,相当于第一个文件用第一行显示,第二个文件全在第二行
[root@localhost dir1]# cat file1
hell
word
1234
[root@localhost dir1]# cat file2
123
vieh
daf
3423432
[root@localhost dir1]# paste file1 file2
hell	123
word	vieh
1234	daf
	3423432

8、tr工具

tr用于字符准换,替换和删除,主要用于删除文件中控制字符或进行字符转换

语法和选项

1、命令的执行结果交给tr处理,其中string1用于查询,string用于转换处理
commands|tr 'string1' 'string2'
2、tr处理的内容来自文件,记住要使用“<”标准输入
tr 'string1' 'string2' <filename
3、匹配string1进行相应操作,如删除操作
tr options 'string1' <filename

常用选项

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

常匹配字符串

字符串含义备注
a-z或[:lower:]匹配所有小写字母如果匹配多个的话[0-9A-Za-z]
A-Z或[:upper:]匹配所有大写字母
0-9或[:digit:]匹配所有数字
[:alnum:]匹配所有数字和字母
[:alpha:]匹配所有字母
[:blank:]所有水平空白行
[:punct:]匹配所有标点符号
[:space:]匹配所有水平或垂直的空格
[:cntrl:]所有控制字符\f ctrl-L 走换行页
\n ctrl-J 换行
\r ctrl-m 回车
\t ctrl-l tab键
使用的时候直接输入这个就好,比如\n

实例:

#把小写字母和数字替换成#
tr 'a-z0-9' '#' <passwd
#删除匹配到的字符
 tr -d 'a-z' <passwd
#删除所有重复出现字符序列,压缩
 tr -s 'a-z'< passwd
练习
#1、提取ip地址,出现空格太多,不好使用cut,可以使用tr的-s压缩命令
[root@localhost dir1]# ifconfig ens33
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.178.129  netmask 255.255.255.0  broadcast 192.168.178.255
        inet6 fe80::344a:b86c:e906:a81c  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:aa:29:4a  txqueuelen 1000  (Ethernet)
        RX packets 54664  bytes 20517979 (19.5 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 33463  bytes 3833682 (3.6 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@localhost dir1]# ifconfig ens33 | grep broadcast |cut -d' ' -f10,12
192.168.178.129 netmask
[root@localhost dir1]# ifconfig ens33 | grep broadcast |cut -d' ' -f10,12|tr -d 'a-zA-Z'
192.168.178.129 

#2、提取所有的地址
[root@localhost dir1]# ifconfig ens33| grep cast
        inet 192.168.178.129  netmask 255.255.255.0  broadcast 192.168.178.255
[root@localhost dir1]# ifconfig ens33| grep cast|tr -d 'a-zA-Z'
         192.168.178.129   255.255.255.0   192.168.178.255

#3、提取所有的地址后,实现换行
(1)
[root@localhost dir1]# ifconfig ens33| grep cast|tr -d 'a-zA-Z'|tr  ' ' '\n'| grep -v '^$'
192.168.178.129
255.255.255.0
192.168.178.255
(2)
[root@localhost hua]# ifconfig ens33 | grep cast | tr -s ' ' |cut -d' ' -f3,5,7
192.168.178.129 255.255.255.0 192.168.178.255
[root@localhost hua]# ifconfig ens33 | grep cast | tr -s ' ' |cut -d' ' -f3,5,7|tr ' ' '\n'
192.168.178.129
255.255.255.0
192.168.178.255


二、bash的特性

1、命令和文件自动补全

Tab只能补全命令和文件

2、常见的快捷键

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

3、常用的通配符(重点)

*:匹配0或多个任意字符
?:匹配任意单个字符
[list]:匹配[list]中的任意单个字符
[!list]:匹配除list中的任意单个字符
{string,string2,.......}:匹配string,string2或更多字符串

4、bash中的引号(重点)

  • 双引号:会把引号的内容当成整体来看待,允许通过¥符号引用其他变量值
  • 单引号:会把引号的内容当成整体来看待,禁止引用其他变量值,shell中特殊符号都被视为普通字符
  • 反撇号:反撇号和$()一样,引号或括号里的命令会优先执行,如果存在嵌套,反撇号不能用

三、SHELL编程模块

一、shell介绍

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HJkIatUu-1638867876874)(C:\Users\HCB\AppData\Roaming\Typora\typora-user-images\image-20210429131342410.png)]

1、编程语言分类
  • 编译型语言:
    程序在执行之前需要一个专门的编译过程,把程序编译成为机器语言文件,运行时不需要重新翻译,直接使用编译的结果就行了。程序执行效率高,依赖编译器,跨平台性差些。如C、C++
  • 解释型语言:
    程序不需要编译,程序在运行时由解释器翻译成机器语言,每执行一次都要翻译一次。因此效率比较低。比如Python/JavaScript/ Perl /ruby/Shell等都是解释型语言。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BZiNF08x-1638867876878)(C:\Users\HCB\AppData\Roaming\Typora\typora-user-images\image-20210429133046281.png)]

  • 总结
    编译型语言比解释型语言速度较快,但是不如解释型语言跨平台性好。如果做底层开发或者大型应用程序或者操作系开发一般都用编译型语言;如果是一些服务器脚本及一些辅助的接口,对速度要求不高、对各个平台的兼容性有要求的话则一般都用解释型语言
2、shell简介

shell介于内核与用户之间,负责命令的解释

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qQ3eSrwJ-1638867876879)(C:\Users\HCB\AppData\Roaming\Typora\typora-user-images\image-20210506130138017.png)]

总结

  • shell就是人机交互的一个桥梁
  • shell的种类
[root@localhost etc]# cat shells 
/bin/sh				#是bash的一个快捷方式
/bin/bash			#bash是大多数linux默认的shell,包含的功能几乎可以涵盖shell所有的功能
/usr/bin/sh
/usr/bin/bash

思考:终端和shell是什么关系

终端是一个程序,提供一个窗口,shell来解析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VIMgCg3U-1638867876881)(C:\Users\HCB\AppData\Roaming\Typora\typora-user-images\image-20210506131102964.png)]

3、shell脚本

(一)什么是shell脚本?

  • —句话概括
    简单来说就是将需要执行的命令保存到文本中,按照顺序执行。它是解释型的,意味着不需要编译。

  • 准确叙述
    若干命令+脚本的基本格式+脚本特定语法+思想= shell脚本

(二)什么时候用到shell脚本?
重复化、复杂化的工作,通过把工作的命令写成脚本,以后仅仅需要执行脚本就能完成这些工作。

(三)shell脚本能干啥?
1、自动化软件部署 LAMP/LNMP/Tomcat…
2、自动化管理 系统初始化脚本、批量更改主机密码、推送公钥…

3、自动化分析处理 统计网站访问量

4、自动化备份 数据库备份,日志转储

4、如何学习shell脚本
  1. 尽可能记忆更多的命令(记忆雒令使用功能和场景)
  2. 掌握脚本的标准的格式(指定魔法字节、使用标准的执行方式运行脚本)
  3. 必须熟悉掌握脚本的基本语法(重点)
5、学习shell脚本的秘诀

多看(看懂)—>模仿(多练)—>多思考(多写)

6、shell脚本的基本写法

1)脚本第一行,魔法字符#!指定解释器【必写】

#!/bin/bash表示以下内容使用bash解释器解析

注意:
如果直接将解释器路径写死在脚本里,可能在某些系统就会存在找不到解释器的兼容性问题,所以可以

使用:#!/bin/env 解释器(bash)

  1. 脚本第二部分,注释(#号)说明,对脚本的基本信息进行描述【可选】
# ! /bin/env bash

# 以下内容是对脚本的基本信息的描述
# Name:名字
# Desc:描述describe
# Path :存放路径
# usage:用法
# update:更新时间

#下面就是脚本的具体内容commands

脚本的两种执行方式:
(1)标准:
#两种标准方式执行脚本,标准执行方式脚本必须要有可执行的权限(建议)
[root@localhost hua]# /data/hua/shell01.sh
hell world!
hell world!
hell world!
[root@localhost hua]# ./shell01.sh 
hell world!
hell world!
hell world!

(2)非标准:
#非标准的执行方法(不建议)前提是脚本没有执行权限
1.直接在命令行指定解释器执行

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

例子:
[root@localhost hua]# bash -x shell01.sh 
+ echo 'hell world!'
hell world!
+ echo 'hell world!'
hell world!
+ echo 'hell world!'
hell world!

2.使用source命令读取脚本文件,执行文件里的代码

例子:
[root@localhost hua]# source shell01.sh 
hell world!
hell world!
hell world!
小试牛刀:练习

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l3nSuvYy-1638867876881)(C:\Users\HCB\AppData\Roaming\Typora\typora-user-images\image-20210507133936855.png)]

[root@localhost hua]# cat shell01.sh 
#! /bin/env bash
# 以下内容是对脚本的基本信息的描述
# Name:shell01
# Desc:第一个脚本
# Path:/data/hua
# usage:/data/hua/shell01.sh
# update:2021-05-07
path=/data/hua/huachuanbin
rm -rf ${path}*
mkdir -p ${path}/dir{1..3}
cp /etc/hosts /data/hua/huachuanbin/dir1
echo "报告,任务已于`date '+%F %T'`时间完成"
echo "$(date +'+%F %T')"
#两种引用命令的方式:
`date '+%F %T'`
$(date +'+%F %T')

第25节

二、变量的定义

1.变量是什么?

—句话概活:变量是用来临时保存数据的,该数据是可以变化的数据。

2.什么时候需要定义变量?
  • 如果某个内容需要多次使用,并且在代码中重复出现,那么可以用变量代表该内容。这样在修改内容的时候,仅仅需要修改变量的值。
  • 在代码运作的过程中,可能会把某些命令的执行结果保存起来,后续代码需要使用这些结果,就可以直接使用这个变量。
3.变量如何定义?

变量名=变量值

变量名:用来临时保存数据的

变量值:就是临时的可变化的数据

[root@localhost hua]# A=hell
[root@localhost hua]# echo $A
hell
[root@localhost hua]# echo ${A}
hell
[root@localhost hua]# A=WORLD
[root@localhost hua]# echo $A
WORLD
[root@localhost hua]# unset A		#取消变量,再次调用是空的
[root@localhost hua]# echo $A


4.变量的定义规则

虽然可以给变量(变量名)赋予任何值;但是,对于变量名也是要求的!

  1. 变量名区分大小写

  2. 变量名不能有特殊符号

    • 比如 ? * #
    • 特别说明:对于有空格的字符串给变量赋值时,要用引号引起来
  3. 变量名不能以数字开头

  4. 等号两边不能有任何空格

  5. 变量名尽量做到见名知意

5、变量的定义方式有哪些
1、基本方式

直接赋值一个变量

[root@localhost hua]# echo $a
12345
[root@localhost hua]# echo ${a}
12345
[root@localhost hua]# echo ${a:2:3}		#表示a变量中第3个字符开始截取,截取3个字符,1代表第0个字符
345

说明:
$变量名和${变量名}的异同
相同点:都可以调用变量
不同点:${变量名}可以只截取变量的一部分,而$变量名不可以

2、命令执行结果赋值给变量
#有两种方式
1、$()
[root@localhost hua]# a=$(hostname)
2、``
[root@localhost hua]# a=`hostname`


#查看系统版本
root@localhost hua]# cat /etc/redhat-release 
CentOS Linux release 7.8.2003 (Core)
#查看内核版本
[root@localhost hua]# uname -r
3.10.0-1127.el7.x86_64


3、交互式定义变量(read)

**目的:**让用户自己给变量赋值,比较灵活

**语法:**read [选项] 变量名

常见选项:

选项释义
-p定义提示用户的信息
-n定义字符数(限制变量值的长度)
-s不显示(不显示用户输入的内容)
-t定义超时时间,默认单位为秒(限制用户输入变量值的超时时间)
举例:
用法一:用户自己定义变量值
#1.普通用法
[root@localhost hua]# read name
huachuanbin
[root@localhost hua]# echo $name
huachuanbin

#2.-p,提示内容
[root@localhost hua]# read -p "请输入名字:" name
请输入名字:huachuanbin
[root@localhost hua]# echo $name
huachuanbin

#3.-s和-p,隐藏输入的内容
[root@localhost hua]# read -s -p "请输入密码:" pass
请输入密码:
[root@localhost hua]# echo $pass
1234567890

#4.显示输入字符长度
[root@localhost hua]# read -n 5 -p  "请输入名字:" name
请输入名字:fhdsa
[root@localhost hua]# echo $name
fhdsa

#5.限制时间,需要在限定时间范围内按下回车才行
[root@localhost hua]# read -t 3 -p "请输入名字:" name
请输入名字:123
[root@localhost hua]# echo $name
123


用法二:变量值来自文件
#先编辑一个ip.txt输入ip地址,然后用read读取
[root@localhost hua]# read -p "请输入ip地址" IP <ip.txt 
[root@localhost hua]# echo $IP
10.213.34.123

4、定义有类型的变量(declare)

**目的:**给变量做一些限制,固定变量的类型,比如:整型、只读

**用法:**declare 选项 变量名=变量值

常用选项:

选项释义举例
-i将变量看成整数declare -i A=23
-r定义只读变量declare -r B=hello
-a定义普通数组;查看普通数组
-A定义关联数组;查看关联数组
-x将变量通过环境导出declare -x AAA=123456 等于export AAA=123456

举例说明:

#定义整数,赋值其他的会变为0
[root@localhost hua]# declare -i a=123
[root@localhost hua]# echo $a
123
[root@localhost hua]# a=hello
[root@localhost hua]# echo $a
0

#定义只读变量
[root@localhost hua]# declare -r a=hello
[root@localhost hua]# echo $a
hello
[root@localhost hua]# unset a
-bash: unset: a: 无法反设定: 只读 variable

#将变量通过环境导出
(1)第一种方法
[root@localhost ~]# aaa=hua
[root@localhost ~]# export aaa
[root@localhost ~]# env | grep aaa
aaa=hua
(2)第二种用declare -x
[root@localhost ~]# declare -x bbb=chuan
[root@localhost ~]# env | grep bbb
bbb=chuan



6、变量的分类
1、本地变量
  • 本地变量:当前用户自定义的变量,当前进程中有效,其他进程及当前进程的子进程无效
[root@localhost ~]# ps						#开启一个终端就是启动一个bash
   PID TTY          TIME CMD
  2087 pts/0    00:00:00 bash
  2102 pts/0    00:00:00 ps
[root@localhost ~]# a=123					#定义一个本地变量,切换到其他用户和子进程则输出不了
[root@localhost ~]# echo $a
123
[root@localhost ~]# /bin/bash				#再开启一个bash,相当于是一个子进程了
[root@localhost ~]# ps
   PID TTY          TIME CMD
  2087 pts/0    00:00:00 bash
  2103 pts/0    00:00:00 bash
  2112 pts/0    00:00:00 ps
[root@localhost ~]# ps -auxf | grep bash	#查看父子关系,可以看到2103是子进程
root       2087  0.0  0.0 115676  2156 pts/0    Ss   18:22   0:00      \_ -bash
root       2103  0.0  0.0 115672  2128 pts/0    S    18:22   0:00          \_ /bin/bash
root       2114  0.0  0.0 112824   984 pts/0    S+   18:22   0:00              \_ grep --color=auto bash

2、环境变量
  • 环境变量:当前进程有效,并且能够被子进程调用
    • env查看当前用户的环境变量
    • set查询当前用户的所有变量(临时变量与环境变量)
    • 定义环境变量(1):export 变量名=变量值 或者 变量名=变量值;export 变量名
    • 定义环境变量(2):declare -x bbb=chuan
3、全局变量
  • 全局变量:全局所有用户和程序都能调用,且继承,新建的用户也默认能调用
  • 解读相关配置文件
  • $HOME:每个用户的家目录
  • 局部和全局变量冲突了,一般以局部变量为准
文件名说明备注
$HOME /.bashrc当前用户的bash信息,用户登录时读取定义别名,umask、函数等
$HOME /.bash_profile当前用户的环境变量,用户登录时读取
$HOME /.bash_logout当前用户退出当前shell时最后读取定义用户退出时执行的程序等
/etc/bashrc全局的bash信息,所有用户都生效定义全局别名,umask、函数等
/etc/profile全局环境变量信息系统和所有用户都生效
$HOME /.bash_history当前用户的历史命令记录

说明:以上文件修改后,都需要重新source让其生效或者退出重新登录

  • 用户登录系统读取相关文件的顺序:
    1. /etc/profile
    2. $HOME /.bash_profile
    3. $HOME /.bashrc
    4. /etc/bashrc
    5. $HOME /.bash_logout
#查看用户的bash信息
[root@localhost ud]# cat .bashrc
# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then		#全局下面有这个文件存在
	. /etc/bashrc				#.等同于source,读取一个文件,刷新
fi

# Uncomment the following line if you don't like systemctl's auto-paging feature:
# export SYSTEMD_PAGER=

# User specific aliases and functions

4、系统变量(内置bash中变量):shell本身已经固定好了他的名字和作用

脚本重点用到的:#?、KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲、*、$@、$1- 9 、 9、 9$

内置变量含义
#?上一条命令执行后返回的状态;状态值为0表示执行正常,非o表示执行异常或错误
$0当前执行的程序或脚本名
$#脚本后面接的参数的个数
$*脚本后面所有参数,参数当成一个整体输出,每一个变量参数之间以空格隔开
$@脚本后面所有参数,参数是独立的,也是全部输出
$1-$9脚本后面的位置参数,$1表示第1个位置参数,依次类推
10 − {10}- 10{n}扩展位置参数,第10个位置变量必须用0大括号括起来(2位数字以上扩起来)
$$当前所在进程的进程号,如echo $$
$!后台运行的最后一个进程号(当前终端)
!$调用最后一条命令历史中的参数

36节

实例:

1、上一条命令执行后返回的状态;状态值为0表示执行正常,非o表示执行异常或错误(#?)
[root@localhost ~]# lll
-bash: lll: 未找到命令
[root@localhost ~]# echo $?
127

2、一些举例使用,$前面有\代表是转义了
#! /bin/env  bash
#name:Variable.sh
#.......

echo "1、当前执行的程序或脚本名:"
echo "\$0=$0"
echo "2、脚本后面接的参数个数:"
echo "\$#=$#"
echo "3、脚本后面所有参数,参数当成一个整体输出,每一个变量参数之间以空格隔开:"
echo "\$*=$*"
echo "4、脚本后面所有参数,参数是独立的,也是全部输出:"
echo "\$@=$@"
echo "5、扩展位置参数,第10个位置变量必须用0大括号括起来(2位数字以上扩起来):"
echo "\${12}=${12}"
echo "6、打印第1个:"
echo "\$1=$1"


3、后台运行的最后一个进程号(当前终端)$!
[root@localhost hua]# ps
   PID TTY          TIME CMD
  2947 pts/0    00:00:00 bash
  3144 pts/0    00:00:00 ps
[root@localhost hua]# jobs				#查看当前终端的后台程序
[root@localhost hua]# sleep 500 &		#程序放到后台去运行
[1] 3145
[root@localhost hua]# jobs
[1]+  运行中               sleep 500 &
[root@localhost hua]# sleep 600			#按下ctrl+z,放到后台暂停
^Z
[2]+  已停止               sleep 600
[root@localhost hua]# jobs
[1]-  运行中               sleep 500 &
[2]+  已停止               sleep 600
[root@localhost hua]# ps
   PID TTY          TIME CMD
  2947 pts/0    00:00:00 bash
  3145 pts/0    00:00:00 sleep
  3147 pts/0    00:00:00 sleep
  3152 pts/0    00:00:00 ps
[root@localhost hua]# echo "$!"
3145

#杀死后台的进程
[root@localhost mail]# jobs
[1]-  运行中               sleep 500 &
[2]+  运行中               sleep 100 &
[root@localhost mail]# kill -9 %1
[root@localhost mail]# jobs
[1]-  已杀死               sleep 500
[2]+  运行中               sleep 100 &


4、调用最后一条命令历史中的参数(!$)
[root@localhost hua]# ls -l
总用量 16
-rw-r--r--. 1 root root   0 514 13:38 1.txt
-rwxr-xr-x. 1 root root  85 58 20:05 2.sh
drwxr-xr-x. 5 root root  42 421 23:47 huachuanbin
-rw-r--r--. 1 root root  14 58 16:17 ip.txt
-rwx--x--x. 1 root root 340 421 23:37 shell01.sh
-rwxr-xr-x. 1 root root 554 58 21:31 variable.sh
[root@localhost hua]# echo !$
echo -l
-l

三、shell的四则运算

算数运算:默认情况下,shell就只能支持简单的整数运算

运算内容:加(+)、减(-)、乘(*)、除(/)、求余数(%)

1、四则运算符号
表达式举例
$(())echo $((1+1))
$[]echo $[10-5]
exprexpr 10 / 5
letn=1;let n+= 等价于 let n=n+1
#举例
#1、$(())的用法举例
[root@localhost hua]# echo $((10*3))
30
#2、$[]的用法举例
[root@localhost hua]# echo $[10*8]
80
#3、expr的用法举例,注意:expr的数值需要空格,并且*需要转义
[root@localhost hua]# expr 10 \* 2
20
#4、let的用法举例
简单用法:
[root@localhost hua]# n=1;let n+=3;echo $n
4
[root@localhost hua]# let n-=1;echo $n
3
求几次方的用法
[root@localhost hua]# n=2;let n=n**3;echo $n
8
[root@localhost hua]# let n=n**3;echo $n
512

2、使用bc工具计算
#1、直接输入bc,输入计算的数值,退出使用quit
[root@localhost hua]# 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.5
2.5
quit

#2、由于一般的方法计算小数点值,会报错,这个时候使用bc工具即可
[root@localhost hua]# $((1+1.5))
-bash: 1+1.5: 语法错误: 无效的算术运算符 (错误符号是 ".5"[root@localhost hua]# echo 1+1.5|bc
2.5
#求平方
[root@localhost hua]# echo 3^2|bc
9

3、了解i++和++i
1、对变量的值的影响
[root@localhost hua]# i=1;j=1;let i++;let ++j;echo $i;echo $j
2
2

2、对表达式的值的影响
[root@localhost hua]# i=1;j=1;
[root@localhost hua]# echo $i;echo $j;
1
1
[root@localhost hua]# let x=i++;let y=++j
[root@localhost hua]# echo $i;echo $j
2
2
[root@localhost hua]# echo $x;echo $y
1
2

四、条件判断语法结构

  • 熟悉条件判断语句,如判断整数、判断字符串等
  • 熟悉流程控制语句基本语法,如if…else…

一、条件判断语法结构

思考:何为真(true),何为假(false)

1、条件判断语法格式
  • 格式1:test条件表达式
  • 格式2:[ 条件表达式 ]
  • 格式3:[[ 条件表达式 ]] 支持正则=-

特别说明:

  1. [ 两边有空格 ]
  2. [[ 两边有空格 ]]
  3. 更多判断,man test去查看,很多条件都是用来进行条件判断
2、条件判断相关参数
(一)判断文件类型
判断参数含义
-e判断文件是否存在(任何类型文件)
-f判断文件是否存在并且是一个普通文件
-d判断文件是否存在并且是一个目录
-L判断文件是否存在并且是一个软连接文件
-b判断文件是否存在并且是一个块设备文件
-s判断文件是否存在并且是一个套接字文件
-c判断文件是否存在并且是一个字符设备文件
-p判断文件是否存在并且是一个命名管道文件
-s判断文件是否存在并且是一个非空文件(有内容)
1、-e判断文件是否存在(任何类型文件)

用$?判断上一条命令执行的结果,0为真,不为0为假

[root@localhost hua]# test -e ./file1
[root@localhost hua]# echo $?
0
[root@localhost hua]# test -e ./test
[root@localhost hua]# echo $?
1
2、-d判断文件是否存在并且是一个目录

用$?判断上一条命令执行的结果,0为真,不为0为假

[ -d /data/hua/huachuanbin/dir1 ];echo $?

[root@localhost hua]# [ -d /data/hua/huachuanbin/dir1 ];echo $?
0
3、-L判断文件是否存在并且是一个软连接文件

[ -L /data/hua/test ];echo $?

先创建一个软链接,用$?判断上一条命令执行的结果,0为真,不为0为假,如果原文件删除了,判断还是真
[root@localhost hua]# ln -s file1 test
[root@localhost hua]# ll
总用量 16
-rw-r--r--. 1 root root   0 514 13:38 1.txt
-rwxr-xr-x. 1 root root  85 58 20:05 2.sh
-rw-r--r--. 1 root root   0 514 16:11 file1
-rw-r--r--. 1 root root   0 514 16:11 file2
-rw-r--r--. 1 root root   0 514 16:11 file3
-rw-r--r--. 1 root root   0 514 16:11 file4
drwxr-xr-x. 5 root root  42 421 23:47 huachuanbin
-rw-r--r--. 1 root root  14 58 16:17 ip.txt
-rwx--x--x. 1 root root 340 421 23:37 shell01.sh
lrwxrwxrwx. 1 root root   5 514 16:26 test -> file1
-rwxr-xr-x. 1 root root 554 58 21:31 variable.sh
[root@localhost hua]# [ -L /data/hua/test ];echo $?
0
4、判断一个目录不存在

不存在则为真

[ ! -d ./dir7 ];echo $?

[root@localhost huachuanbin]# ll
总用量 0
drwxr-xr-x. 2 root root 19 421 23:47 dir1
drwxr-xr-x. 2 root root  6 421 23:47 dir2
drwxr-xr-x. 2 root root  6 421 23:47 dir3
[root@localhost huachuanbin]# [ ! -d ./dir7 ];echo $?
0

(二)判断文件权限
判断参数含义
-r当前用户对其是否可读
-w当前用户对其是否可写
-x当前用户对其是否可执行
-u是否有suid,高级权限冒险位
-g是否sgid,高级权限强制位
-k是否有t位,高级权限粘滞位
实例:
test -r shell01.sh ;echo $?
[ -x huachuanbin ];echo $?

(三)判断文件新旧

说明:这里的新日指的是文件的修改时间。三=

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

实例:

[root@localhost hua]# [ file1 -nt file2 ];echo $?;
0
[root@localhost hua]# [ file1 -ot file2 ];echo $?;
1
[root@localhost hua]# [ file1 -ef file2 ];echo $?;
1

(四)判断整数
判断参数含义
-eq相等
-ne不等
-gt大于
-lt小于
-ge大于等于
-le小于等于
(五)判断字符串
判断参数含义
-z判断是否为空字符串,字符串长度为0则成立
-n判断是否为非空字符串,字符串长度不为0则成立
string1 = string2判断字符串是否相等
string 1 != string2判断字符串是否不相等

实例:

#四种用法
[root@localhost hua]# test -z "hell world";echo $?
1
[root@localhost hua]# test -n "hell world";echo $?
0
[root@localhost hua]# test "hello" = "world";echo $?
1
[root@localhost hua]# test "hello" != "world";echo $?
0

(六)多重条件判断
判断符号含义举例
-a 和&&逻辑与[ 1 -eq 1 -a 1 -ne 0 ] [ 1-eq 1 ]&&[ 1 -ne 0 ]
-o 和||逻辑或[ 1 -eq 1 -o 1 -ne 1] [ 1 -eq 1]

特别说明:

&& 前面的表达式为真,才会执行后面代码

|| 前面的表达式为假,才会执行后面的代码

; 只用于分割命令或表达式

实例:

#注意:多个运算符在一起的时候,没有优先级,从左往右,
[root@localhost ~]# [ $(id -u) -eq 0 ] && echo "admin"||echo "is not admin"
admin
#这里切换到普通用户了
[ud@localhost ~]$ [ $(id -u) -eq 0 ] && echo "admin"||echo "is not admin"
is not admin
#前面表达式为假,执行了BBB的,然后BBB为真,则输出了CCC
[ud@localhost ~]$ [ 1 -eq 2 ] && echo AAA || echo BBB && echo CCC 
BBB
CCC
#前面表达式为假,执行了AAA,然后AAA为真,输出了后面两个
[ud@localhost ~]$ [ 1 -eq 2 ] || echo AAA && echo BBB && echo CCC
AAA
BBB
CCC
(1)类C风格的数值比较
注意:在(())中,=表示赋值;==表示判断
[root@server ~]# ((1==2)) ;echo $?
[root@server ~]#((1<2)) ;echo $?
[root@server ~]#((2>=1)) ;echo $?
[root@server ~]#((2!=1)) ;echo $?
[root@server ~]# ((`id -u`==0) ) ;echo $?
[root@server ~]# ((a=123)) ;echo $a
[root@server ~]# unset a
[root@server ~]#((a==123)) ;echo $?

(2)字符串比较

如果其中一个字符串为空的话,字符串判断要加双引号,或者用两层中括号括起来的时候也可以,没有双引号会报错

注意:双引号引起来,看作一个整体;===[字符串]比较中都表示判断
[root@server ~]# a='he11o wor1d ' ; b=wor1d
[root@server ~]# [ $a = $b ] ; echo $?
[rootaserver ~]#[ "$a" = "$b” ];echo $?
[root@server ~]#[ "$a" != "Sb” ] ;echo $?
[root@server ~]# [ "$a" !== "$b” ];echo $?			错误,要用上面这个格式
[root@server ~]# [ "$a" == "Sb”];echo $?
[root@server ~]# test "$a" != "$b " ;echo $?

实例:

[root@localhost hua]# A=hello;B=world;[ "$A" = "$B" ];echo $?
1
[root@localhost hua]# A=hello;B=world;[ "$A" != "$B" ];echo $?
0
[root@localhost hua]# A=hello;B=world;[ "$A" == "$B" ];echo $?
1
#判断一个文件是否存在,并且是链接文件,有这三种写法,第二三种需要加两个[],或者分开
[root@localhost hua]# [ -e test -a -L test ];echo $?
0
[root@localhost hua]# [[ -e test && -L test ]];echo $?;
0
[root@localhost hua]# [ -e test ] &&[ -L test ];echo $?;
0

(3)逻辑运算符总结

语法结构:

(1)test expr1

(2)[ expr1 ]

(3)[[ expr1 ]]

条件判断:

-d 、-L、-e、-w、-ef、-n、-z、

-s:判断文件是否不为空;!=判断文件是否为空文件(通过man test查询得到)

1.符号;和&&和H都可以用来分割命令或者表达式
2.分号(;)完全不考虑前面的语句是否正确执行,都会执行;号后面的内容
3.&&符号,需要考虑&&前面的语句的正确性,前面语句正确执行才会执行&&后的内容;反之亦然
4.||符号,需要考虑| |前面的语句的非正确性,前面语句执行错误才会执行||后内容;反之亦然

5.如果&&和一起出现,从左往右依次看,按照以上原则

二、流程控制语句

1、if结构

注意:只要正确,就要一直往前冲

F:表示false,为假

T:表示true,为真

if [ condition ];then
		command
		command
fi

if test 条件;then
	命令
fi

[ 条件 ] && command
2、if-else结构

注意:分叉入口,二选一

if	[ condition ];then
		command1
	else
		commend2
fi

[ 条件 ] && command1 ||command2

实例:

#! /bin/env bash

if [ "$1" = "hello" ];then
echo " 正确!"
else
echo "错误"
fi

练习
#题目:提示用户自己输入hello,然后输出helloworld,否则输出请重新输入hello
#! /bin/env bash
read -p "请输入hello:" str
if [ "$str" = "hello" ];then
	echo "$str,world!"
else
	echo "请重新输入hello"
fi

3、if…elif…else结构

注意:选择很多,能走的只有一条

if [ condition1 ];then
	command1	结束
elif [ condition2 ];then
	command2	结束
else
	command3
fi


注释:
如果条件1满足,执行命令1后结束;如果条件1不满足,再看条件2,如果条件2满足执行命令2后结束;如果条件1和条件2都不满足执行命令3结束.

实例:

#! /bin/env bash
if [ 1 -eq 2 ];then
        echo a
elif [ 2 -ne 2 ];then
        echo b
elif [ 3 -gt 3 ];then
        echo c
else
        echo 4
fi

#结果输出为4
4、层层嵌套结构
if [ condition1 ] ;then
	command1						#满足了前面的if则会执行这个
		if [ condition2 ];then
			command2
		fi
else
		if [ condition3 ];then
			command3
		elif [ condition4 ] ;then
			command4
		else
			command5fi
fi

注释:
如果条件1满足,执行命令1;如果条件2也满足执行命令2,如果不满足就只执行命令1结束;
如果条件1不满足,不看条件2;直接看条件3,如果条件3满足执行命令3;如果不满足则看条件4,如果条件4满足执行命令4;否则执行命令5

三、应用案例

(一)判断两台主机是否ping通
#	ping -c1			可以指定ping的次数
#	&>/dev/null		ping的结果不要了,丢到黑洞里面去
#	& 					表示 等同于 的意思

脚本实现:
#! /bin/env bash
read -p "请输入要ping的ip地址:" ip
ping -c1 $ip &>/dev/null
if [ $? -eq 0 ]
then
echo "当前主机可以和$ip ping通"
else
echo "当前主机不可以和$ip ping通"
fi

#自己写的:
#! /bin/env bash
read -p "请输入要ping的ip地址:" ip
ping -c1 $ip
if [ $? = 0 ]
then
echo "可以ping通"
else
echo "ping不通"
fi

(二)判断一个进程是否存在
1、实现:
自己写的
#! /bin/env bash
read -p "请输入要判断程序是否在的程序名:" ip
	ps -ef | grep $ip | grep -v grep &>/dev/null
if [ $? -eq 0 ]
	then
	echo "当前进程存在"
else
	echo "当前进程不存在"
fi

或者
test $? -eq 0 && echo "进程存在" || echo "进程不存在"

2、补充命令pgrep

举例:pgrep httpd

pgrep命令:以名称为依据从运行进程队列中查找进程,并显示查找到的进程id选项
-o:仅显示找到的最小(起始)进程号;
-n:仅显示找到的最大(结束)进程号;
-1:显示进程名称;
-P:指定父进程号;pgrep -p 4764 查看父进程下的子进程id
-g:指定进程组;
-t:指定开启进程的终端;
-u:指定进程的有效用户ID。

(三)判断一个服务是否正常
1、有三种请求web页面的方式
  1. wget:下载页面

    • 用法:wget www.baidu.com
  2. curl:请求页面

    • 用法:curl www.baidu.com
  3. elinks:

    • 用法,交互式:elinks www.baidu.com
    • 用法,只返回结果:elinks --dump www.baidu.com
2、实现
#1、判断服务是否可用
#! /bin/env bash
read -p "请输入要判断的网址:" ip
        curl $ip &>/dev/null
if [ $? = 0 ]
then
        echo "输入的网址可以提供服务"
else
        echo "输入的网址不可提供服务"
fi

或者:
[ $? = 0 ] && echo "可以提供服务" || echo "不可以提供服务"

#2、下载网址,并删除下载文件
#! /bin/env bash
read -p "请输入要判断的网址:" ip
	wget -P /data/hua/huachuanbin $ip &>/dev/null
if [ $? = 0 ]
	then
	echo "下载成功"
	rm -r /data/hua/huachuanbin/index*
else
	echo "下载失败"
fi

(四)练习
1、判断用户是否存在
#自己写的
#! /bin/env bash
read -p "请输入要查找的用户名:" name
	cat passwd |cut -d: -f1,3 | grep -w $name
if [ $? -eq 0 ]
	then
	echo "你输入的用户名为:$name,是存在的,id是:$a,na是:$b"
else
	echo "你输入的用户名为:$name,该用户是不存在的"
fi

#视频里的
#! /bin/env bash
read -p "请输入要查找的用户名:" name
id $name &>/dev/null

if [ $? -eq 0 ]
        then
        echo "你输入的用户名为:$name,是存在的,id是:$a,na是:$b"
else
        echo "你输入的用户名为:$name,该用户是不存在的"
fi

四、总结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kw8iqepO-1638867876882)(C:\Users\HCB\AppData\Roaming\Typora\typora-user-images\image-20210523230719874.png)]

58节

五、循环

(一)、for循环的列表循环

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

1、打印1到5
#打印1到5
#! /bin/env bash
for var in 1 2 3 4 5	#这里或者是{1..5}
 do
        echo $var
 done
2、打印1到50之间的偶数
#1、打印1到50之间的偶数
#! /bin/env bash
for i in {1..50}
 do 
	a=$(($i%2))			#这里可以用a=$[$i%2],或者a=`expr $i % 2`
	if [ $a -eq 0 ]
	then
		echo $i
	fi
 done
 
#2、用一条命令打印1到100的奇数,这里的2代表步长为2:
for i in {1..100..2};do echo $i; done

3、从10打印到1
#从10打印到1
(1)for i in {10..1};do echo $i;done
(2)seq 10 -1 1							#-1为步长
(3)for i in {10..1..-1};do echo $i;done

(二)、for循环的不带列表循环

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

基本语法格式:

for variable
	do
		command
		command
		.....
	done
	

举例说明:

[root@localhost hua]# cat test.sh 
#! /bin/env bash
for i
do 
	echo "hello,$i"
done
[root@localhost hua]# ./test.sh 1
hello,1
[root@localhost hua]# ./test.sh 1 2 3 4 5
hello,1
hello,2
hello,3
hello,4
hello,5
[root@localhost hua]# ./test.sh {1..5}
hello,1
hello,2
hello,3
hello,4
hello,5

(三)、for循环的类C结构

接班语法机构

for(( expr1;expr2;expr3))
	do
		command
		command
		.....
	done
	
for ((i=1;i<=10;i++))
 do
        echo "hello,$i"
 done
 
 expr1:定义变量并赋初值
 expr2:决定是否进行循环(条件)
 expr3:决定循环变量如何改变,决定循环什么时候退出
1、应用案例:脚本计算1到100之间的奇数和(包含continue)

思路:

1.定义一个变量来保存奇数的和I
⒉.找出1-100的奇数,保存到另一个变量里
3.从1-100中找出奇数后,再相加,然后将和赋值给变量

4.遍历完毕后,将sum的值打印出来

#自己写的,最终输出2500
#! /bin/env bash
sum=0
for ((i=1;i<=100;i++))
do
        a=$((i%2))
        if [ $a -gt 0 ]
        then
        sum=$((sum+$i))

        fi
done
echo $sum

#方法一:
#! /bin/env bash
sum=0
for i in {1..100..2}
do
        let sum=`expr $sum + $i`
done
echo $sum

#方法二
#! /bin/env bash
sum=0
for ((i=1;i<=100;i+=2))
do 
	let sum=`expr $sum + $i`
done
		
echo $sum

#方法三
#! /bin/env bash
sum=0
for ((i=1;i<=100;i++))
do 
	if [ $[$i%2] -ne 0 ]
	then
		let sum=$sum+$i
	fi
done	
echo $sum 

或者 
test $[$i%2] -ne 0 && sum=`expr $sum + $i
test $[$i%2] -ne 0 && let sum=$sum+$i

#方法四,continue
#! /bin/env bash
sum=0
for ((i=1;i<=100;i++))
do
        if [ $[$i%2] -eq 0  ]
        then
        continue
        else
        let sum=$sum+$i
        fi
done
echo $sum


(四)、循环控制语句

continue:继续;表示循环体内下面的代码不执行,重新开始下一次循环

break:打断;马上停止执行本次循环,执行循环体后面的代码
exit:表示直接跳出程序

实例:

1、continue用法
#1、打印奇数和
#! /bin/env bash
sum=0
for ((i=1;i<=100;i++))
do
        if [ $[$i%2] -eq 0  ]
        then
        continue
        else
        let sum=$sum+$i
        fi
done
echo $sum

#2、结果是i输出6,并且除了2,创建了1345
#! /bin/env bash
for ((i=1;i<=5;i++))
do
	test $i -eq 2 && continue ||touch /data/hua/file$i.doc
done
echo $i


2、break用法
#i=2后,跳出循环,并创建了file.doc和输出echo $i
#! /bin/env bash
for ((i=1;i<=5;i++))
do
	test $i -eq 2 && break ||touch /data/hua/file$i.doc
done
echo $i

3、exit用法
#创建了两个文件,到第三个直接退出程序,所以不会执行echo $i
#! /bin/env bash
for ((i=1;i<=5;i++))
do
	test $i -eq 3 && exit ||touch /data/hua/file$i.doc
done
echo $i

(五)、总结

1、判断一个目录是否存在
test -d /data/hua/huachuanbin/ ;if [ $? -eq 0 ];then echo "存在";fi
2、判断一个目录是否存在,如果没有则创建
test -d /data/hua/huachuanbin/ ;if [ $? -eq 0 ];then echo "存在";else mkdir  /data/hua/huachuanbin;fi
3、判断一个目录是否不存在,不存在则创建
[ ! -d /data/hua/huachuanbin ] && mkdir /data/hua/huachuanbin || echo "存在"
4、用if判断,判断一个目录是否不存在,不存在则创建
if [ ! -d /data/hua/huachuanbin ] ;then mkdir  /data/hua/huachuanbin ;else echo "存在";fi
5、判断一个文件是否存在并且不为空,空的话删除 ! -s代表为空文件
if [ ! -s /data/hua/huachuanbin/1.txt ] ;then rm  -rf /data/hua/huachuanbin/1.txt;else echo "不为空";fi

(六)、应用案例

1、判断输入的一个数是否为质数
质数只能被1和他本身整除,有第三中情况的都不是质
数

#! /bin/env bash
#定义变量来保存用户输入的数字
read -p "请输入一个数字:" number

#先排除用户输入的1和2
[ $number -eq 1 ]&& echo "你输入为1,重新输入" &&exit
[ $number -eq 2 ]&& echo "你输入为2,重新输入" &&exit

#判断用户,seq这里再除以2,为了优化,这样能节省一半,因为可以被2整除就可以节省一半
for i in `seq 2 $[$number-1]`
do 
	 [ $[$number%$i] -eq 0 ] && echo "不是质数" && exit
	
done
echo "是质数"

2、批量创建用户

mkpasswd -l 8 #代表随机生成8位密码

#1、echo 123 |passwd --stdin u$i更新用户密码信息,适用于shell脚本
#2、useradd  -g 不为用户创建新组, 而使用 -g 后的指定用户组,-G  后面跟一个用户组列表,使用户属于指定的多个用户组。

#! /bin/bash
grep -w ^class /etc/group &>/dev/null
[ $? -ne 0 ]&& groupadd class

for ((i=1;i<=5;i++))
do
        
        useradd -G class u$i
        echo 123 |passwd --stdin u$i
        
done
3、检查主机网络是否OK+并发执行
1、检查主机网络是否OK
自己写的
#! /bin/bash
ip=192.168.178
for i in {1..300}
do 
	ping -c1 $ip.$i &>/dev/null
	if [ $? -eq 0 ]
	then
	echo "可以ping通:$ip.$i" >> ok.txt 
	else 
	echo "不可以ping通:$ip.$i" >> not.txt 
	fi

done
2、并发执行

并发执行:{程序}&表示将程序放到后台并行执行,如果需要等待程序执行完毕再进行下面内容,需要加wait

1、查看脚本执行时间
[root@localhost hua]# time ./tesh.sh 

real	0m24.322s
user	0m0.003s
sys	0m0.057s


2、并发执行,在循环体加{}&,wait是等待,等待上一个执行才执行下一个,这样可以极大节省时间,并发执行
#! /bin/bash
ip=192.168.178
for i in {1..300}
do
{ 
        ping -c1 $ip.$i &>/dev/null
        if [ $? -eq 0 ]
        then
        echo "可以ping通:$ip.$i" >> ok.txt
        else
        echo "不可以ping通:$ip.$i" >> not.txt
        fi
}&
done
wait
echo "所有测试完毕"

(七)、for循环总结

  • for循环语法结构

    • 列表
    • 非列表
    • 类c风格
  • for循环可以结合条件判断和流程控制语句

    • do…done 循环体
    • 循环体里可以是命令的集合,再加上条件判断依据流程控制
  • 控制循环语句

    • continue 继续,跳过本次循环,继续下一次循环

    • break 跳出循环,执行循环体外的代码

    • exit 退出,直接退出程序

(八)、while循环

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

1、while循环语法结构
#1、格式:
while  表达式
	do
		command
	done

#2、举例:
while  [ 1 -eq 1 ]或者(( 1>2))
	do
		command
	done


#3、从1加到5

#! /bin/bash
i=1
while [ $i -le 5 ]
	do
		echo $i
		let i++	
	done 

或者,类c风格
 i=1;while(($i<=10));do echo $i;let i+=2;done


2、应用案例
1、打印1到50之间偶数和
自己写的
#打印1到50之间偶数和
#! /bin/bash
i=1
sum=0
	while [ $i -le 50 ]
		do
			if [ $[$i%2] -eq 0 ]
			then
				sum=$[$sum+$i]
			fi
let i++	

		done 
echo $sum



#方法一:

#! /bin/bash
sum=0

for ((i=0;i<=50;i+=2))
do
        let sum=$sum+$i
done
echo $sum


#方法二:
#! /bin/bash
sum=0
i=2
while [ $i -le 50 ]
do
        let sum=$sum+$i				或者:sum=$[$sum+$i]
        let i+=2							i=$[$i+2]
       
done
echo $sum

2、脚本同步系统时间,并发送邮件到邮箱

自己写的:

#! /bin/bashntp=120.25.115.20num=0while truedo 	ntpdate $ntp	if [ $? -ne 0 ]		then		num=$[$num+1]		echo "在时间$(date)同步失败,次数是$num" |mail -s "ntp同步失败"  1549245143@qq.com		sleep 6	else		num=$[$num+1]			if [ $[$num%5] -eq 0 ]			then 			 echo "在时间$(date)同步成功,次数是$num次" |mail -s "ntp同步"  1549245143@qq.com			fi	fidone

视频实例:

#! /bin/bash
ntp=192.168.178.130
count=0
a=0
while true
do
        ntpdate $ntp &>/dev/null				#同步该ntp服务器的时间
        if [ $? -ne 0 ]
        then
                echo "ntp service connect failed" |mail -s "check ntp service" 1549245143@qq.com								#如果报错,则输出错误信息到指定邮箱,echo后面的为内容,后面的-s后面的为标题,发送到本地服务器内用root@localhost,路径在/var/spool/mail
                let a++							#实现报错5次就退出程序
                if [ $a -eq 5 ]
                then
                        exit
                fi
        else

                let count++
                if [ $count -eq 100 ]     # 或者$[$count%100] -eq 0,则后面不需要置0,count=0
                then
                        echo "ntp service connect success" |mile -s "check ntp service" 1549245143@qq.com && count=0
                fi
        fi


sleep 3
done

同步时间:

#1、按下ctrl+r,输入ntpdate 会自动显示配置文件的ntp服务器,回车即可(这里用回以前默认的配置文件)
[root@localhost etc]# ntpdate 1.centos.pool.ntp.org
 9 Jun 14:25:18 ntpdate[2309]: adjust time server 203.107.6.88 offset 0.001046 sec
 
#2、xinetd服务
(1)安装
yum -y install xinetd
(2)启动
 service xinetd start
(3)进入配置文件
/etc/xinetd.d
(4)打开开关
time-dgram文件,这里改为no
# This is for quick on or off of the service
        disable         = no
(5)查看端口
 netstat -nltup | grep 37
(6)用rdate同步时间( rdate命令用于显示其他主机的日期与时间。)
rdate -s 0.centos.pool.ntp.org

(7)linux主机查看邮件的位置
/var/spool/mail

(8)修改脚本为rdate -s 0.centos.pool.ntp.org

(9)测试,关掉xinetd服务,然后一直tail -f /var/spool/mail/root
 service xinetd stop





(8)由于是个死循环,放后台执行的方法
./tesh.sh &
查看用jobs
杀死用kill -9 %1			#这里百分之一要看前面括号里面的值

(九)、until循环

1、语法结构
until expression	[ 1 -eq 1 ]  ((1>=1))	do		commnd		commnd	done
2、实例
#(1)
#! /bin/bash
i=5
until [ $i -lt 1 ]
do
        if [ $i -eq 100 ]
        then 
        exit
        fi

        echo $i
        let i++
done


#(2)
i=5;until (($i<1));do echo $i;let i--;done
3、应用案例
1、需求

(1)使用until语句批量创建10个用户,要求stu1-stu2分别为1001-1005

(2)stu6-stu10用户的家目录分别在/rhome/stu6-/rhome/stu10

2、思路

(1)创建用户语句

useradd -u 指定用户ID号。该值在系统中必须是唯一的

useradd -d 指定用户登入时的主目录,替换系统默认值/home/<用户名>

(2)使用循环语句until批量创建用户

(3)判断用户前五个和后五个

3、实现

要想简练脚本的话,两者合为一起,里面用ifelse

#! /bin/bash
test -d /rhome && echo "存在" || mkdir /rhome;echo "不存在,已创建"
i=1
until [ $i -gt 5 ]
do
	useradd -u $[1000+$i] stu$i
	echo 123|passwd --sdtin stu$i
	let i++
done

until [ $i -gt 10 ]
do
	useradd -d /rhome/stu$i stu$i
	echo 123|passwd --sdtin stu$i
	let i++
done

(十)、随机数(RANDOM)

系统变量:RANDOM,默认会产生0-332767的随机整数

打印一个随机数echo $RANDOM查看系统上一次生成的随机数set | grep RANDOM产生0~1之间的随机数echo $[ $RANDOM%2]产生0~2之间的随机数echo $[ $RANDOM%3]
案例1,随机产生139开头的1000个电话号码:

需求:随机产生以139开头的电话号码,产生一个phonenum.txt文件,随机产生以139开头的手机号1000个,每个一行。

思路:

  1. 产生1000个号码,脚本要循环1000次
  2. 139+8位,后八位随机产生,可以让每一位数字都随机产生
  3. 将随机产生的数字分别保存到变量里,然后加上139保存到文件里

作者写的:

判断是否有重复

[root@localhost hua]# cat phonenum.txt |wc -l
1001
[root@localhost hua]# cat phonenum.txt |sort -u |wc -l
1001

给重复的排序

[root@localhost hua]# sort phonenum.txt |uniq -c|sort -nr |head

#! /bin/bashfile=/data/hua/phonenum.txtfor ((i=0;i<=1000;i++))do 	n1=$[$RANDOM%10]	n2=$[$RANDOM%10]	n3=$[$RANDOM%10]	n4=$[$RANDOM%10]	n5=$[$RANDOM%10]	n6=$[$RANDOM%10]	n7=$[$RANDOM%10]	n8=$[$RANDOM%10]		echo $i  139$n1$n2$n3$n4$n5$n6$n7$n8 >>$file	done

自己写的,可以实现

#! /bin/bashtest -f /data/hua/phonenum.txt	if [ $? -ne 0 ]	then	touch /data/hua/phonenum.txt	else 	echo "文件存在"	fifile=phonenum.txtfor ((i=0;i<=1000;i++))do 	echo $i  139$[$RANDOM%10]$[$RANDOM%10]$[$RANDOM%10]$[$RANDOM%10]$[$RANDOM%10]$[$RANDOM%10]$[$RANDOM%10]$[$RANDOM%10] >>$file	done 
案例2,随机抽取5位幸运观众

需求:

1、在上面的1000个里面抽奖4个幸运观众,显示出这5个幸运观众

2、单只显示头3位数和尾号的4位数,中间用*代替

思路:

1、确定幸运观众所在的行,0-1000 随机找出一个数字 ,从1开始加1 [ [ [RANDOM%1000+1]

2、将电话号码提取出来

3、显示前3个和后4个到屏幕

#! /bin/bashphone=/data/hua/phonenum.txtfor i in {1..5}do	line=`cat $phone |wc -l`								#获取这个文件的行号	luck=`head -$[$RANDOM%$line+1] $phone |tail -1`			#获取号码	echo 第$i个幸运号码是:${luck:0:3}****${luck:7:4}			#变量截取字符,并中间用*表示	sed -i "/$luck/d"  $phone								#删除抽中的行	done
案例3,批量创建用户(密码随机生成)

需求:批量创建5个用户,每个用户的密码为一个随机数

思路:

  1. 循环5次创建用户
  2. 产生一个密码文件来保存用户的随机密码
  3. 从密码文件中去除随机密码赋值给用户
#! /bin/bashecho user{1..5}:Lins$[$RANDOM%9000+1000]|tr -s  ' ' '\n' >>user.pass	#生成账户和随机密码保存到文件,密码是四位数以上的,范围是1000-9999for i in {1..5}do	user=`head -$i user.pass |tail -1 |cut -d: -f1`			#截取文件里的用户	pass=`head -$i user.pass |tail -1 |cut -d: -f2`			#截取文件里的密码	useradd $user 											#添加用户	echo $pass |passwd --stdin $user						#设置密码done
随机数补充知识
#1、随机数永远是两位数echo $[$RANDOM%90+10]	#求余100才是0-99,由于是两位需要加10 ,相当于0加10,90加10,这样就是10-99范围的数字

(十一)、循环总结

for循环

#1、列表循环for i in {1..5} ;do echo $i; done#2、类C风格for ((i=1;i<=5;i++));do echo $i; done

while循环

#1、普通版i=1;while [ $i -le 5 ];do echo $i ;let i++; done#2、类c风格i=1;while(($i<=5));do echo $i ;let i++; do

until循环

#1、普通版i=1 ;until [ $i -gt 5 ];do echo $i;let i++ ;done#2、类c风格i=1 ;until(($i>5));do echo $i;let i++ ;done

(十二)、嵌套循环

关键字:大圈套小圈

**时钟:**分针与秒针,秒针一圈,分针转一格,循环嵌套就是外层循环一次,内层循环一轮。

  1. 一个循环体内又包含另一个完整的循环结构,称为循环的嵌套
  2. 每次外部循环都会触发内部循环,直至循环完成,才接着执行下一次外部循环
  3. for循环、while循环和until循环可以相互嵌套
(一)、应用案例,打印图案
1、打印图案1

1
12
123
1234
12345

#1、脚本实现,用for类c风格#! /bin/env bashfor ((i=1;i<=5;i++))do        for ((j=1;j<=$i;j++))        do                echo -n $j		#-n代表不换行        doneecho done输出:112123123412345#2、脚本实现,用while的类c风格,结合#! /bin/env bashfor ((i=1;i<=5;i++))do        j=1        while (($j<=$i))        do        echo -n $j        let j++        doneechodone
2、打印图案2

12345

1234

123

12

1

#! /bin/env bashfor ((i=1;i<=5;i++))do        for ((j=1;j<=6-$i;j++))        do                echo -n $j		#-n代表不换行        doneecho done
3、打印图案3

5
54
543
5432
54321

#! /bin/env bash
for ((i=1;i<=5;i++))
do
        for ((j=5;j>=6-$i;j--))	#第一个输出5,5大于等于5,输出第一个5
        do
                echo -n $j		#-n代表不换行
        done

echo 
done
4、打印图案4

54321

5432

543

54

5

#! /bin/env bashfor ((i=1;i<=5;i++))do        for ((j=5;j>=$i;j--))        do                echo -n $j              #-n代表不换行        doneechodone

(十三)、阶段性补充总结

1、变量定义
(1)变量名=变量值
echo $变量名
echo ${变量名}

(2)read -p "提示用户信息:"  变量名

(3)declare -i/-x/-r	变量名=变量值

2、流程控制语句
(1)if [ 条件判断 ];then	command	fi	(2)if [ 条件判断 ];then	command	else		command	fi(3)if [ 条件判断1 ];then	command1	elif [ 条件判断2 ];then	command2	fi
3、循环语句
forwhileuntil
4、影响shell程序的内置命令
exit		退出整个程序break		结束当前循环,或跳出本层循环continue	忽略本次循环剩余的代码,直接进行下一次循环shift		是位置参数向左移动,默认移动是1位,可以使用shift 2
shift命令
./ tesh.sh 1 2 3 4 5相当于先加了1,然后把2位移到左边第一位,以此类推,如果shift为2 那就是位移两位,可以加1到10的奇数#! /bin/env bashsum=0while [ $# -ne 0 ]do         let sum=$sum+$1	#加第一个参数$1shiftdoneecho $sum

六、expect脚本

expect 自动应答,tcl语言

要安装这个命令:expect

为什么要设置set timeout时间,是由于网络问题需要等待一下

检查是否已经安装:yum list | grep expect安装:yum -y install expect查看 :rpm -ql expect,可以看到/usr/bin/expect,所以在脚本里面需要用这个解释器解析,首行是这个

执行命令(显示调用):expect tesh.sh

实例:
**需求1:**A远程登录到server上什么都不做

实例:

脚本执行方式:#./expect1.sh#/shell04/expect1.sh#expect -f expect1.sh(1)登录上去什么不做#!/usr/bin/expect#开启一个程序spawn ssh root@192.168.178.130#捕获相关内容expect {        "(yes/no)?" {send "yes\r";exp_continue}        "password:" {send "123456\r"}}interact //交互,这个是登录上去什么都不做,如果没有这个的话,会自动退出(2)登录上去,使用变量#!/usr/bin/expectset ip 192.168.178.130	set pass 123456			set timeout 5	#开启一个程序spawn ssh root@$ip#捕获相关内容expect {	"(yes/no)?" {send "yes\r";exp_continue}	"password:" {send "$pass\r"}}interact //交互
**需求2:**登录上去操作东西
(1)登录主机删除相关内容,并创建文件
#!/usr/bin/expect
set ip 192.168.178.130	
set pass 123456			
set timeout 5	

#开启一个程序
spawn ssh root@$ip
#捕获相关内容
expect {
	"(yes/no)?" {send "yes\r";exp_continue}
	"password:" {send "$pass\r"}

}

expect "#"		#这里也可以用跟上面一样的花括号,因为这里只要捕获一个#,所以这样写也可以
send "rm -rf /tmp/*\r"
send "touch /tmp/file{1..3}.txt\r"
send "date\r"
send "exit\r"
expect eof				#代表程序结束


(2)登录上去创建用户和删除东西
#!/usr/bin/expect
set ip 192.168.178.131
set pass 123456
set timeout 3

spawn ssh root@$ip
expect {
        "(yes/no)?" {send "yes\r";exp_continue}
        "password:" {send "$pass\r"}
}
expect {
        "#" {send "useradd expectest;touch /tmp/expctest;exit\r"}
}
expect eof
需求3、shell脚本和expect结合使用,在多台服务器创建用户
#实现远程登录主机创建用户并删除文件
#!/bin/bash
while read ip pass			#ip地址和密码来自下面的ip.txt
do
        /usr/bin/expect <<-END &>/dev/null	#-是制表符,如果没有的话,下面的END要左靠齐,<<看到下面的END后会结束程序
        spawn ssh root@$ip		#开启一个程序
        expect {
        "yes/no" { send "yes\r";exp_continue }
        "password:" { send "$pass\r" }
}
        expect "#" { send "useradd hua;rm -rf /tmp/*;exit\r" }	#exit退出去再回车
        expect eof			#代表这个程序结束 /usr/bin/expect但还是撤不出来,所以后面要敲一个END,看到他完全撤出
        END
echo "$ip服务器用户创建完毕"
done <ip.txt

1、expect脚本定义位置参数

实例:

#执行的时候需要加上ip和密码,$argv 0,$argv 1相当于第一个和第二个参数,传参
#!/usr/bin/expect
set ip [ lindex $argv 0 ]
set pass [ lindex $argv 1 ]		
set timeout 5	

#开启一个程序
spawn ssh root@$ip
#捕获相关内容
expect {
	"(yes/no)?" {send "yes\r";exp_continue}
	"password:" {send "$pass\r"}

}
interact //交互

2、bash结合expect使用

实例1:实现远程创建用户和删除东西
#实现远程登录主机创建用户并删除文件#!/bin/bashwhile read ip pass			#ip地址和密码来自下面的ip.txtdo        /usr/bin/expect <<-END &>/dev/null	#-是制表符,如果没有的话,下面的END要左靠齐,<<看到下面的END后会结束程序        spawn ssh root@$ip		#开启一个程序        expect {        "yes/no" { send "yes\r";exp_continue }        "password:" { send "$pass\r" }}        expect "#" { send "useradd hua;rm -rf /tmp/*;exit\r" }	#exit退出去再回车        expect eof			#代表这个程序结束 /usr/bin/expect但还是撤不出来,所以后面要敲一个END,看到他完全撤出        ENDecho "$ip服务器用户创建完毕"done <ip.txt#直接复制过去可以使用的#!/bin/bashwhile read ip passdo	/usr/bin/expect <<-END &>/dev/null	spawn ssh root@$ip	expect {	"yes/no" { send "yes\r";exp_continue }	"password:" { send "$pass\r" }}	expect "#" { send "useradd huachuanbin;rm -rf /tmp/*;exit\r" }	expect eof	ENDecho "$ip服务器用户创建完毕"done < ip.txt
实例2:推送公钥到可以ping的主机

简单实现步骤:

#1、先在主机A创建yunwei用户,并切入该账户,生成秘钥对ssh-keygen 提示后一直输入回车即可ssh-keygen -P '' -f id_rsa	#这里可以跳过提示直接生产文件,但是不会生成.ssh目录#2、进入存放公钥的隐藏目录cd /home/yunwei/.ssh可以看到公钥是id_rsa.pub#3、复制公钥到B主机ssh-copy-id root@B在B服务器中的/root/.ssh目录下面有authorized_keys文件,该文件内容是和A服务器的id_rsa.pub一致#4、这样就可以实现主机A可以免密登录主机B
1、具体需求

写一个脚本,将跳板机上yunwei用户的公钥推送到局域网内可以ping通的所有机器上

说明:主机和密码文件已经提供

2、案例分析
  • 1、跳板机上的yunwei用户生成密钥对

    • 账号是否存在(id yunwei)
    • 判断该用户是否有秘钥对文件[ -f xxx ]
  • 2、判断expect程序是否安装

  • 3、判断局域网内主机是否ping通(循环判断)

    • 循环判断 for while
    • 循环体do…done ping主机,如果ping通调用expect程序,实现自动应答推送公钥
  • 4、测试验证是否免密登录

  • 把生成的公钥推送到能够ping通的主机

  • 测试验证是否免密登录成功

  • 循环判断局域网内主机是否ping通

  • 检查

3、前期准备
  1. 环境准备
jumper-server 有yunwei用户yunwei用户sudo授权登录root用户执行visudo命令,加上yunwei## Allow root to run any commands anywhereroot    ALL=(ALL)       ALLyunwei	ALL=(root)		NOPASSWD:ALL,!/sbin/shutdown,!/sbin/init,!/bin/rm -rf /这段话意思是ALL=(root)以管理员root身份去做事情,不要输入密码NOPASSWD:ALL,不要关机!/sbin/shutdown,不要关机!/sbin/init,不要删除根下面的所有文件!/sbin/shutdown解释说明:1)第一个字段yunwei指定的是用户:可以是用户名,也可以是别名。每个用户设置一行,多个用户设置多行,也可以将多个用户设置成一个别名后再进行设置。2)第二个字段ALL指定的是用户所在的主机:可以是ip,也可以是主机名,表示该sudo设置只在该主机上生效,ALL表示在所有主机上都生效!限制的一般都是本机,也就是限制使用这个文件的主机;一般都指定为"ALL"表示所有的主机,不管文件拷到那里都可以用。比如: 10.1.1.1=...则表示只在当前主机生效。3)第三个字段(root)括号里指定的也是用户:指定以什么用户身份执行sudo,即使用sudo后可以享有所有root账号下的权限。如果要排除个别用户,可以在括号内设置,比如ALL=(ALL , ! orac1e, !pos).4)第四个字段ALL指定的是执行的命令:即使用sudo后可以执行所有的命令。除了关机和删除根内容以外;也可以设置别名。NOPASSwD:ALL表示使用sudo的不需要输入密码。5)也可以授权给一个用户组%admin ALL=(ALL) ALL表示admin组里的所有成员可以在任何主机上以任何用户身份执行任何命令#!/bin/env bash#判断公钥是否存在[ ! f /home/yunwei/.ss/id_rsa ] && ssh-keygen -P '' -f ~/.ssh/id_rsa#循环判断主机是否ping通,如果Ping通推送公钥tr ':' ' ' </home/yunwei/ip.txt | while read ip passdo{	ping -c1 $ip $>/dev/null	if [ $? -eq 0 ];then	echo $ip >> ~/ip_up.txt	/usr/bin/expect <<-END &> /dev/null	spawn ssh-copy-id root@ip	expect {			"yes/no" { send "yes\r";exp_continue }			"password:" {send "$pass\r" }		}	expect eof	END	fi}&donewaitecho "公钥已经推送完毕,正在测试........"#测试验证remote_ip=`tail -1 ~/ip_up.txt`ssh root@$remote_ip hosname &>/dev/nulltest $? -eq 0 && echo "公钥成功推送完毕"
4、代码拆分和实现

功能一:管理员创建用户和安装expect软件包

#!/bin/env bashhome_dir=/home/yunwei/#判断yunwei账号是否存在{id yunwei[ $? -ne 0 ]&& useradd yunwei && echo 123456|passwd --stdin yunwei} &> /dev/null#判断expect程序是否安装rpm -q expect[ $? -ne 0 ] && yum -y install expect && echo "expect 软件已经成功安装"

功能二:切换运维用户判断主机是否ping通,并且运维用户推送公钥

(1)for循环写法
#!/bin/env bash
#判断密钥对是否存在(输完这一步,切换到命令行模式,输入set list,END前面会有一个^I,这个就是制表符)

#判断是否存在秘钥文件,没有则生成,并生成到目录/home/yunwei/.ssh/下面
home_dir=/home/yunwei
[ ! -f $home_dir/.ssh/id_rsa.pub ] && ssh-keygen -P '' -f $home_dir/.ssh/id_rsa &>/dev/null

#循环检查主机的网络并且进行公钥推送
ip_txt=$home_dir/ip.txt
for i in `cat $ip_txt`
do
	ip=`echo $i|cut -d: -f1`
	pass=`echo $i|cut -d: -f2`
	ping -c1 $ip &>/dev/null
	if [ $? -eq 0 ];then
		/usr/bin/expect <<-END
		set timeout 10
		spawn ssh-copy-id root@$ip
		expect "yes/no" { send "yes\r";exp_continue }
		expect "password:" { send "$pass\r" }
			expect eof
		END

	else
		echo $ip >>$home_dir/ip_down.txt
	fi

done


(2)while循环写法
#!/bin/env bash
#判断密钥对是否存在(输完这一步,切换到命令行模式,输入set list,END前面会有一个^I,这个就是制表符)
home_dir=/home/yunwei
[ ! -f $home_dir/.ssh/id_rsa.pub ] && ssh-keygen -P '' -f $home_dir/.ssh/id_rsa &>/dev/null

#循环检查主机的网络并且进行公钥推送
ip_txt=$home_dir/ip.txt
while read ip pass
do
	ping -c1 $ip &>/dev/null
	if [ $? -eq 0 ];then
		/usr/bin/expect <<-END
		set timeout 10
		spawn ssh-copy-id root@$ip
		expect "yes/no" { send "yes\r";exp_continue }
		expect "password:" { send "$pass\r" }
			expect eof
		END

	else
		echo $ip >>$home_dir/ip_down.txt
	fi

done</home/yunwei/ip.txt

注释:
for循环用的ip.txt文件内容是192.168.178.129:123456,以冒号分割
while循环用的ip.txt文件内容是192.168.178.129 123456,以空格分割

5、推送成功测试公钥是否推送成功
#!/bin/env bash#判断密钥对是否存在(输完这一步,切换到命令行模式,输入set list,END前面会有一个^I,这个就是制表符)home_dir=/home/yunwei[ ! -f $home_dir/.ssh/id_rsa.pub ] && ssh-keygen -P '' -f $home_dir/.ssh/id_rsa &>/dev/null#循环检查主机的网络并且进行公钥推送while read ip pass      #把:号变成空格,处理的数据来自于ip.txt,并赋值给while的变量do        ping -c1 $ip &>/dev/null        if [ $? -eq 0 ];then                echo $ip >> ~/ip_up.txt                /usr/bin/expect <<-END &>/dev/null		set timeout 10                spawn ssh-copy-id root@$ip                expect {                        "yes/no" { send "yes\r";exp_continue }                        "passwd:" { send "$pass\r" }                        }                expect eof		END        else                echo $ip >>$home_dir/ip_down.txt        fidone</home/yunwei/ip.txtremote_ip=`head -1 ~/ip_up.txt`ssh root@$remote_ip hostname[ $? -eq 0 ] && echo "公钥推送成功"

七、数组

1、数组的定义

1、普通数组只能使用整数作为数组索引(元素的下标)2、关联数组可以使用字符串作为数组索引(元素的下标)

2、普通数组的定义

  • 一次赋予一个值
数组名[索引下标]=值
arrary[0]=v1
arrary[1]=v2
arrary[2]=v3
arrary[3]=v4
  • 一次赋予多个值
数组名=(值1 值2 值3 .....)
array=(var1 var2 var3)

array1=(`cat /etc/passwd`)		#将文件中每一行赋值给array1数组,注意要把密码文件的空格去掉:sed  's/ //g' /etc/passwd把空格替换为没有空格
array2=(`ls /root`)
array3=(harry amy jack "Miss Hou")
array4=(1 2 3 4 "hell world" [10]=linux)

3、数组的读取

${数组名[元素下标]}

echo ${array[0]}			#获取数组里第一个元素
echo ${array[*]}			#获取数组里的所有元素
echo ${#array[*]}			#获取数组里的所有元素个数
echo ${!array[@]}			#获取数组元素的索引下标
echo ${array[@]:1:2}		#访问指定的元素;1代表从下标为1的元素开始获取,2代表获取后面几个元素


查看普通数组信息:
declare -a

4、关联数组的定义

(1)首先声明关联数组
declare -A asso_array1
declare -A asso_array2
declare -A asso_array3
(2)数组赋值
  • 一次赋一个值
数组名[索引or下标]=变量值
#asso_array1[linux]=one
#asso_array1[java]=two
#asso_array1[linux]=three

查看:declare -A
  • 一次赋多个值
asso_array2=([name1]=harry [name2]=jack [name3]=amy [name4]="miss you")

5、关联数组其他定义方式

let方式,可以定义下标和值

[root@localhost ~]# declare -A books			#定义数组
[root@localhost ~]# let books[linux]++			#定义一个数组名和变量值
[root@localhost ~]# declare -A					#查看数组信息
declare -A books='([linux]="1" )'
[root@localhost ~]# let books[linux]++			#在当前数组在执行++
[root@localhost ~]# declare -A
declare -A books='([linux]="2" 

[root@localhost ~]# let books[java]+=5			#定义一个数组名,变量值直接为5
[root@localhost ~]# declare -A
declare -A books='([java]="5" [linux]="2" )'

113节

6、变量定义补充

1、取出一个目录下的目录和文件:dirname和basename
[root@localhost hua]# A=/root/huachuanbin/hua/a.txt				#一个文件目录和文件赋予给A[root@localhost hua]# echo $A/root/huachuanbin/hua/a.txt[root@localhost hua]# dirname $A								#取出目录/root/huachuanbin/hua[root@localhost hua]# basename $A								#取出文件a.txt
2、变量"内容"的删除和替换
一个"%"代表从右往左去掉一个/key/两个"%%"代表从右往左最大去掉/key/一个"#"代表从左往右去掉一个/key/两个"##"代表从左往右最大去掉/key/举例:[root@localhost hua]# url=www.taobao.com[root@localhost hua]# echo ${#url}		#输出多少个字节14[root@localhost hua]# echo ${url#*.}	#从左往右去掉一个key,以点为分割taobao.com[root@localhost hua]# echo ${url##*.}	#从左往右最大去掉key,以点为分割,输出comcom[root@localhost hua]# echo ${url%.*}	#从右往左去掉一个key,以点为分割,*通配符,代表.后面的值www.taobao[root@localhost hua]# echo ${url%%.*}	#从右往左最大去掉key,以点为分割www

7、实战案例

写一个脚本,统计web服务的不同连接状态个数

1、找出查看网站连接的状态的命令
两者的命令执行结果是一样的,监听状态的顺序不一致
netstat -antp | grep 80
ss -antp | grep 80|awk -F' ' '{print $1}'

2、如何统计不同的状态,循环去统计,需要计算
#!/bin/env bash
declare -A status
s=`ss -antp | grep 80|awk -F' ' '{print $1}'`
for i in $s
do
	let status[$i]++
done


for j in ${!status[*]}
do
	echo $j: ${status[$j]}
done

输出结果为:
[root@localhost ~]# ./test.sh 
ESTAB: 1
TIME-WAIT: 2
LISTEN: 1

八、case语句

1、case语句为多重匹配语句

2、如果匹配成功,执行相匹配的命令

1、语法结构

说明:pattern表示需要匹配的模式

case var in							#定义变量;var代表是变量名
pattern 1#模式1;用|分割多个模式,相当于or,比如可以这样 hell|workd|h)
	commamd1						#需要执行的语句
	;;								#两个分好代表命令结束
pattern 2)
	commamd2
	;;
pattern 3)
	commamd3
	;;
		*)							#default,不满足以上模式,默认执行*)下面的语句
	commamd4
	;;
esac								#esac表示case语句结束

2、应用案例

(1)脚本传不同值做不同事
#!/bin/env bash
case $1 in
start|s)
	service  httpd start &>/dev/null && echo "service is starting....."
	;;
stop|t)
	service  httpd stop &>/dev/null && echo "service is stoping......"
	;;
restart|r)
	service  httpd restart &>/dev/null && echo "service is restarting........."
	;;
	*)
	echo "请输入你要的动作"
	;;
esac

(2)根据用户需求选择做事
作者写的:两层case,实现指定的服务和动作
#!/bin/env bash
read -p "请输入需要管理的服务名称(ftp|httpd):" service
case $service in

httpd|apache)
	read -p "请输入要操作的动作:" action
	case $action in
		start|s)
		service  httpd start &>/dev/null && echo "service is starting....."
		;;
		stop|t)
		service  httpd stop &>/dev/null && echo "service is stoping......"
		;;
		restart|r)
		service  httpd restart &>/dev/null && echo "service is restarting........."
		;;
	esac
	;;
vsftpd|ftp)
		echo "ftp is running"
		;;
		*)
		echo "请输入你要管理的服务名称ftp|httpd"
		;;
esac

自己写的,可以实现用户自己输入服务,然后输入动作
#!/bin/env bash
read -p "请输入需要管理的服务名称:" service


	read -p "请输入要操作的动作:" action
	case $action in
		start|s)
		service  $service start &>/dev/null && echo "$service service is starting....."
		;;
		stop|t)
		service  $service stop &>/dev/null && echo "$service service is stoping......"
		;;
		restart|r)
		service  $service restart &>/dev/null && echo "$service service is restarting........."
		;;
		*)
		echo "请输入动作(start|stop|restart)"
	esac

(3)菜单提示让用户选择需要做的事情

需求:

模拟一个多任务维护界面;当执行程序时先显示总菜单

**********请选择**********h	显示命令帮助f	显示磁盘分区d	显示磁盘负载m	查看内存使用u	查看系统负载q	退出程序*************************

思路:

1、打印菜单

2、交互式让用户输入操作编号,然后做出相应处理

1、cat可以处理标准输入的内容,并打印出来,如下遇到END则结束,并输出
[root@localhost ~]# cat <<END
> HE
> LOO
> END
HE
LOO

2、切换用户执行命令后还会回到当前用户
(1)切换用户执行东西
[root@localhost ~]# su - yunwei <<END
> echo hello
> END
上一次登录:三 91 03:05:34 CST 2021pts/0 上
hello

(2)在免密登录的主机上执行建立文件
[root@localhost ~]# ssh root@192.168.178.130<<END
> cd /home/huachuanbin
> touch test.test
> END

落地实现:

每次都会打印菜单在上面,并且执行一个命令之前会清屏

#!/bin/env bash
#1、菜单打印
cat <<EOF
**********请选择**********
h	显示命令帮助
f	显示磁盘分区
d	显示磁盘负载
m	查看内存使用
u	查看系统负载
q	退出程序
*************************
EOF
#2、用户选择内容

while read -p "请选择需要操作的内容(help h)" action
do
clear											#清屏
cat <<-EOF
        **********请选择**********
        h       显示命令帮助
        f       显示磁盘分区
        d       显示磁盘负载
        m       查看内存使用
        u       查看系统负载
        q       退出程序
        *************************
EOF

case $action in
	h|help)
	cat <<-EOF
	**********请选择**********
	h       显示命令帮助
	f       显示磁盘分区
	d       显示磁盘负载
	m       查看内存使用
	u       查看系统负载
	q       退出程序
	*************************
	EOF
	;;
	f)
	lsblk
	;;
	d)
	df -h
	;;
	m)
	free -h
	;;
	u)
	uptime
	;;
	q)
	exit
	;;
	*)
	echo "请输入正确值"
esac
done

九、函数

1、什么是函数

  • shell中允许将一组命令集合语句形成一段可用代码,这些代码块称为shell函数
  • 给这段代码起个名字称为函数名,后续可以直接调用该端代码的功能

2、如何定义函数

方法1:

函数名()
{
	函数体(一堆命令的稽核,来实现某个功能)
}

方法2:

function 函数名()
{
	函数体(一堆命令的集合,来实现某个功能)
}

函数中return说明:

  1. return可以结束一个函数,类似于循环控制语句break(结束当前循环,执行循环体后面的代码)
  2. return默认返回函数中最后一个命令状态值,也可以给定参数值,范围是0-256之间
  3. 如果没有return名,函数将返回最后一个指令的退出状态值
function 函数名()
{
	函数体(一堆命令的集合,来实现某个功能)
	echo hello
	return					#1、return可以结束一个函数
	echo world
	return 10				#2、return默认返回函数中最后一个命令状态值,也可以给定参数值,用$?可以看到是10
}

3、函数如何调用

(1)当前命令行调用
#专门写一个函数文件,然后sourc这个文件(重新读取),直接输入函数名会出现函数里的结果
#!/bin/env bash
hello()
{	
	echo "is hello function!"
}

haha()
{
        echo $1						#定义一个传参的函数

}


[root@localhost ~]# source test.sh 
[root@localhost ~]# hello
is hello function!
[root@localhost ~]# haha 888
888

(2)定义到用户的环境变量中
1、定义到用户的环境变量下,局部变量
[root@localhost ~]# vi ~/.bashrc
文件中增加如下内容
menu()
{
cat <<EOF
**********请选择**********
h	显示命令帮助
f	显示磁盘分区
d	显示磁盘负载
m	查看内存使用
u	查看系统负载
q	退出程序
*************************
EOF
}

haha()
{
	echo $1

}

注意:当用户打开bash的时候会读取该文件

2、定义到全局的变量下
把函数编写到下面
vi /etc/bashrc
(3)脚本中调用
1、简单的脚本调用
#!/bin/env bash
hello()
{	
	echo "is hello function!"
}

haha()		#调用函数

2、一个脚本调用其他脚本函数
#!/bin/env bash
hello()
{	
	echo "is hello function!"
}

source /root/test.sh					#读取另外一个脚本
hello;world								#调用本脚本和其他脚本函数

4、应用案例

需求:

  1. 写一个脚本收集用户输入的基本信息(姓名,性别,年龄)如不输入一直提示输入
  2. 最后根据用户的信息输出相对应的内容

思路:

  1. 交互式定义多个变量来保存用户信息

  2. 如果不输入一直提示输入

    • 循环直到输入字符串不能为空
    • 每个信息都必须不能为空
  3. 根据用户输入信息做出匹配判断

#简单实现用户输入姓名,如不输入一直提示输入
#!/bin/bash
input_fun()
{
input_var=""							#先定义一个输入的变量为空值
output_var=$1							#把第一个参数复制给输出的变量
while [ -z $input_var ]					#判断输入变量是否为空
do
	read -p "$output_var" input_var		#读取第一个参数的值(请输入你的姓名:),变量为输入变量
done
echo $input_var							#如果输入变量有值,那么直接输出值


}

input_fun 请输入你的姓名:				#这个代表函数的第一个参数$1

脚本最终实现

#1、最终实现连续输入三个内容,如不输入一直提示输入(第一种方法)
#!/bin/bash
input_fun()
{
input_var=""							#先定义一个输入的变量为空值
output_var=$1							#把第一个参数复制给输出的变量
while [ -z $input_var ]					#判断输入变量是否为空
do
	read -p "$output_var" input_var		#读取第一个参数的值(请输入你的姓名:),变量为输入变量
done
echo $input_var							#如果输入变量有值,那么直接输出值


}

name=`input_fun 请输入你的姓名:`
sex=`input_fun 请输入你的性别:`
old=`input_fun 请输入你的年龄:`
echo 姓名结果:$name
echo 性别结果:$sex
echo 年龄结果:$old



#2、最终实现连续输入三个内容,如不输入一直提示输入(第二种方法),并使用case
#!/bin/bash
fun()
{
        read -p "$1" name
        if [ -z $name ];then
        fun $1
        else
        echo $name
        fi
}

#调用函数并且获取用户的姓名、性别、年龄分别赋值给na、sex、age
na=`fun 请输入你的姓名:`
sex=`fun 请输入你的性别:`
age=`fun 请输入你的年龄:`
echo $na
echo $sex
echo $age

#通过性别判断年龄阶段
case $sex in
man|m)
	if [ $age -gt 18 -a $age -le 35 ];then
	echo "你是中年大叔!"
	elif [ $age -gt 35 ];then
	echo "你是老年人"
	else
	echo "你是年轻人"
	fi
	;;
woman|w)
	if [ $age -gt 18 -a $age -le 35 ];then
        echo "你是中年大妈!"
        elif [ $age -gt 35 ];then
        echo "你是老年大妈"
        else
        echo "你是女孩"
        fi
        ;;
*)
	echo "请输入m或者w判断"
	;;
esac

5、综合案例

1、任务背景

现有跳板机虽然实现了统一入口来访问生产服务器,yunwei用户权限太大可以操作跳板机上的所有目录文件,存在数据被误删除的安全隐患,所有希望做一些安全策略来保证跳板机的正常使用

2、具体需求

  1. 只允许yunwei用户通过跳板机远程连接后台的应用服务器做一些维护操作
  2. 公司运维人员远程通过yunwei用户连接跳板机时,跳出以下菜单供选择
欢迎使用jumper-server,请选择你要操作的主机:
1.DB1-Master
2.DB2-Slave
3.web1
4.web2
h.hlep
q.exit

3、综合分析

  1. 将菜单定义为一个函数,方便后面调用
  2. 将脚本放大yunwei用户家目录里的.bashrc文件里面
  3. 用case语句实现用户的选择 [交互式定义变量]
  4. 当用户选择了某一台服务器后,进一步询问用户需要做的事情,case…esac
  5. 使用循环来实现用户不选择一直让其选择
  6. 限制用户退出后直接关闭终端exit

脚本实现

实现登录运维账号先提示菜单供用户选择
把以下脚本文件放到yunwei账号家目录/home/yunwei,在运维的家目录/home/yunwei/.bashrc添加以下内容:
/home/yunwei/t.sh			#执行脚本
exit						#当执行完脚本q键后,执行exit命令,就可以实现当用户操作完之后直接退出终端

#!/bin/env bash
#菜单打印
menu()
{
cat <<EOF
******请选择******
1.DB1-Master
2.DB2-Slave
3.web1
4.web2
h.hlep
q.exit
*****************
EOF

}
trap :  2										#屏蔽信号,当按下ctrl+c没有任何反应
menu

while true
do
read -p "请选择你要访问的主机:" host
case $host in
1)
	ssh root@192.168.178.130 source 1.sh			# 在这末尾加source 1.sh代表读取130主机的这个脚本,把一些功能写进130主机,这样就能实现跨主机访问 
	;;
2)
	ssh root@192.168.178.131
	;;
3)
	echo "您已进入web1"
	;;
4)	
	echo "您已进入web2"
        ;;
h)
	echo "hlep文档"
	;;
q)
	exit
	;;
*)
	echo 请重新输入
	;;
esac
done


回顾信号:

查看:kill -l
1、sighub		重新加载配置
2、sigint		键盘终端ctrl+c
3、sigquit		键盘退出
9、sigkill		强制终止
15、sigterm		终止(正常结束),缺省信号
18、sigcont		继续
19、sigstop		停止
20、sigtstp		暂停ctrl+z

十、正则表达式

1、正则表达式是什么?

正则表达式(Regular Expression、regex或regexp,缩写为RE),也译为正规表示法、常规表示法,是一种字符模式,用于在查找过程中匹配指定的字符
许多程序设计语言都支持到用正则表达式进行字符串操作。例如,在Perl中就内建了一个功能强大的正则表达式引擎。
正则表达式这个概念最初是由Unix中的工具软件(例如sed和grep)普及开的。支持正则表达式的程序如: locate | find | vim| grep | sed | awk

2、正则能干什么?

  1. 匹配邮箱,身份证号码,手机号,银行卡号
  2. 匹配某些特定字符串,做特定处理等等

3、正则当中名词解释

  • 元字符

    指那些在正则表达式中具有特殊意义的专有字符,如点(.)、星(*)、问号(?)等

  • 前导字符

    位于元字符前面的字符 abc* aooo.

4、第一类正则表达式

(1)正则中普通常用的元字符
元字符功能备注
.匹配除了换行符以外的任意单个字符前导字符出现0次或连续多次任意长度字符grep ‘g.’ 1.txt
*前导字符出现0次或连续多次grep ‘g*’ 1.txt ,没有g的也会打印出来,因为是0次或者多次,如果是grep ‘gg*’ 1.txt,要匹配gg才可以,因为第一个g是固定死了(g后面有*), grep ‘gg.*’ 1.txt,匹配gg后面的的点星,后面啥都可以匹配
.*任意长度字符ab.*
^行首(以…开头)grep ‘^g.’ 1.txt,匹配g开头的并且后面有一个字符
$行尾(以…结尾)grep ‘e$’ 1.txt
^$空行
[]匹配括号里任意单个字符或一组单个字符grep ‘[dg]’ 1.txt,匹配d或者g
[^]匹配不包含括号里任一单个字符或一组单个字符grep ‘【^gd】’ 1.txt,匹配不包含gd的行,区别在于内容和gd】
1匹配以括号里任意单个字符或一组单个字符开头grep ‘2’ 1.txt,匹配d或者g开头
[]匹配不以括号里任意单个字符或一组单个字符开头grep 'gd】'1.txt,匹配不以g或者d开头的行,区别在于开头
(2)正则中其他常用元字符

注意,这里有\杠,被隐藏了

元字符功能备注
<取单词的头grep ‘<go’ 1.txt,匹配go开头
>取单词的尾
< >精确匹配grep ‘<googgle532>’ 1.txt,精确匹配,相当于grep -w
{n }匹配前导字符连续出现n次grep ‘g{3}’ 1.txt,匹配g连续出现3次
{n,}匹配前导字符至少出现n次
{n,m}匹配前导字符出现n次与m次之间grep ‘[0-9]{3}’ 1.txt,匹配数字出现3次,grep ‘[0-9]{3}.[0-9]{3}.[0-9]{3}.[0-9]{3}’ 1.txt,匹配ip地址
( )保存被匹配的字符
\d匹配数字(grep -P)grep -P ‘\d’ 1.txt
\w匹配字母数字下划线(grep -P)[a-zA-Z0-9],grep -P ‘\w’ 1.txt
\s匹配空格、制表符、换页符(grep -P)[\t\r\n]

举例说明:

1、进入:模式,%s代表全文搜索(1)输入%s/192.168.178.130/192.168.178.129/g,实现替换ip地址(2)输入%s/\(192.168.178\).129/\1.666/g,保存192.168.178,替换129位666,这里的后面\1代表就是括号里的前三位地址(2)helloworld yourself替换为yourworld helloself,\1代表第一个保存被匹配的字符,\2代表第二个%s/hello\(world\) your\(self\)/your\1hello\2/g2、[root@localhost ~]# grep '\<hua' passwd			#匹配单词开头3、[root@localhost ~]# grep '\<huachu\>' passwd	#精确匹配4、[root@localhost ~]# grep 'go\{2\}' 1.txt 		#匹配o出现2次googlegoogglegoooooglegooooooogle5、[root@localhost ~]# grep  '[0-9]\{2\}' 1.txt 	#匹配数据出现过两次fdago2344oglegooggle532gooooooogl342355e6、grep -P '\d' 1.txt 						#匹配数字
(3)扩展类正则常用元字符
  • grep要用,必须加-E或者让你兄弟egrep找我
  • sed要用,必须加-r
扩展元字符功能备注
+匹配一个或多个前导字符bo+匹配boo、bo
匹配0个或一个前导字符bo?匹配b、bo
|匹配a或者b
()组字符(看成整体)(my|your)self,表示匹配mysql或者匹配yourself
{n}前导字符重复n次
{n,}前导字符重复至少n次
{n,m}前导字符重复n到m次

举例说明:

grep -E "g+" 1.txt 			#匹配g为1个或多个grep -E 'go?' 1.txt			#匹配o为0次或多次,前面有g固定了,所有有g但没有o的也会打印grep -E 'g.?' 1.txt 		#g固定死了后面任意一个字符出现0次或多次grep -E '^e|^f' 1.txt		#匹配e开头或者f开头的行grep -E '^(f|e)' 1.txt 		#匹配e开头或者f开头的行,()表示看成整体grep -E 'go{2}' 1.txt		#匹配o重复出现至少2次,相当于不加-E的grep  'go\{2\}' 1.txt 

5、第二类正则表达式

表达式功能示例
[:alnum:]字母与数字字符[[:alnum:]]+
[:alpha:]字母字符(包括大小写字母)[[:alpha:]]{4}
[:blank:]空格与制表符[[:blank:]]*
[:digit:]数字[[:digit:]]?
[:lower:]小写字母[[:lower:]]{4,}
[:upper:]大写字母[[:upper:]]+
[:punct:]标点符号[[:punct:]]
[:space:]包括换行符,回车等在内的所有空白[[:space:]]+

示例:

grep -E '[[:digit:]]?' 1.txt			#匹配数字出现0次或1次grep -E '[[:alnum:]]{5}' 1.txt			#匹配字母与数字出现5次,想当于grep -E '[a-z0-9A-Z]{5}' 1.txt

6、正则表达式总结

把握一个原则,让你轻松搞定可恶的正则符号

  1. 我要找什么?

    • 找数字 [0-9]
    • 找字母 [a-zA-Z]
    • 找标点符号 [[punct:]]
  2. 我要如何找

    • 以什么为首 ^key
    • 以什么结尾 key$
    • 包含什么或不包含什么 [abc] 3 [尖号abc] 4
  3. 我要找多少

    • 找前导字符出现0ci或连续多次 ab*
    • 找任意单个(一次)字符 ab.
    • 找任意字符 ab.*
    • 找前导字符连续出现几次 {n} {n,m} {n,}
    • 找前导字符出现1次或多次 go+
    • 找前导字符出现0次或者1次 go?

实例:

1、匹配数字出现2次grep '[0-9]\{2\}' 1.txtgrep -E  '[0-9]{2}' 1.txt grep '[0-9]\{3,5\}' 1.txt				#匹配数字出现3到5次grep '[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}.' 1.txt			#匹配ip地址grep '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' 1.txt 		#匹配ip地址grep -E '([0-9]{1,3}\.){3}.[0-9]{1,3}' 1.txt 					#匹配ip地址,看成整体()用法grep -P '\d{1,3}\.\d{1,3}.\d{1,3}.\d{1,3}' 1.txt				#匹配ip地址,-P和\d用法grep -P '\d+\.\d+\.\d+\.\d+' 1.txt 								#匹配ip地址,-P和\d用法2、匹配不以#开头的行grep '^[^#]' version-groups.conf 

7、练习作业

#1、查找不以大写字母开头的行(三种写法)。grep '^[^A-Z]' 1.txtgrep -v '^[A-Z]' 1.txtgrep '^[^[:upper:]]' 1.txt #2、查找有数字的行(两种写法)grep -P '\d' 1.txtgrep  '[0-9]' 1.txt#3、查找一个数字和一个字母连起来的grep '[0-9][a-zA-Z]' 1.txtgrep -E '[0-9][a-zA-Z]|[a-zA-Z][0-9]' 1.txt #4、查找不以r开头的行grep '^[^r]' 1.txtgrep -v '^r' 1.txt #5、查找以数字开头的grep  '^[0-9]' 1.txt#6、查找以大写字母开头的 grep  '^[A-Z]' 1.txt #7、查找以小写字母开头的 grep  '^[a-z]' 1.txt #8、查找以点结束的grep  '\.$' 1.txt #9、去掉空行grep -v '^$' 1.txt#10、查找完全匹配abc的行 grep '\<abc\>' 1.txt grep 'abc' 1.txt grep -w 'abc' 1.txt #11、查找A后有三个数字的行grep -E 'A[0-9]{3}' 1.txtgrep  'A[0-9]\{3\}' 1.txt #12、统计root在/etc/passwd里出现了几次grep -o 'root' 1.txt|wc -l#13、用正则表达式找出自己的IP地址、广播地址、子网掩码ifconfig ens33| grep -o -E '([0-9]{1,3}\.){3}[0-9]{1,3}'ifconfig ens33|grep -o -P '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'ifconfig ens33|grep -o -P '(\d{1,3}\.){3}\d{1,3}'ifconfig ens33|grep -o -P '(\d{1,3}\.){3}\d+'#14、找出文件中的ip地址并打印替换成172.16.2.129ifconfig ens33|grep -o -P '(\d{1,3}\.){3}\d+'|sed -n 's/192.168.178.\(129\)/172.16.2.\1/p' #15、找出文件中的ip地址ifconfig ens33|grep -o -P '(\d{1,3}\.){3}\d+'#16、找出全部是数字的行grep -E '^[0-9]+$' 1.txt #17、找出邮箱地址grep -E '^[0-9]+@[a-z0-9]+\.[a-z]+$' 1.txt 					#匹配@前面只是数字的grep -E '^([0-9]|[a-z])+@[a-z0-9]+\.[a-z]+$' 1.				#匹配@前面有数字和字母的grep -E '^[0-9a-zA-Z]+@[a-z0-9]+\.[a-z]+$' 1.txt 			#匹配@前面有数字和字母的

  1. ↩︎

  2. dg ↩︎

  3. abc ↩︎

  4. 尖号abc ↩︎

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值