Shell脚本 - 高阶
Shell高级
正则表达式
- 上一章说过通配符的功能,结合系统命令进行模糊查询,跟find结合用来查询指定名称的文件名
- 而正则表达式是在文件中匹配符合条件的字符串,常跟grep命令结合进行精确匹配
- 扩展正则是对基础正则的补充,结合grep时需要用-E选项,或直接用egrep命令
基础正则表达式 | |
元字符 | 作用 |
. | 匹配除换行符以外的任意一个字符 |
* | 前一个字符匹配0或任意多次 |
^ | 匹配行首 |
$ | 匹配行尾 |
[] | 匹配中括号中指定的任意一个字符 |
[^] | 匹配除中括号的字符以外的任意一个字符 |
\ | 转义符,用于取消特殊符号的含义 |
扩展正则表达式 | ||
扩展元字符 | 作用 | |
+ | 匹配除换行符以外的任意一个字符 | |
? | 前一个字符匹配0次或1次 | |
{n} | 表示其前面一个字符恰好出现n次 | |
{n,} | 表示其前面一个字符出现不小于n次 | |
{n,m} | 表示前面的一个字符至少出现n次,最多出现m次 | |
{,m} | 表示前面的一个字符最多出现m次 | |
| | 匹配两个或多个分支选择 | |
() | 匹配()内的整体,可以理解为由多个单个字符组成的大字符 |
准备测试数据
#grep命令复习
功能:在指定文件中过滤包含指定字符串的行
格式:grep 选项 ”关键词“ 文件名
选项:
-A 数字 #列出符合条件的行,并连续列出后续n行。
-B 数字 #列出符合条件的行,并连续列出前面n行。
-c #统计符合条件的字符串行数
-i #忽略大小写
-n #输出行号
-v #反向查找(取反)
-o #只列出关键字
-w #强制匹配和字符串一样的
-E #使用扩展正则表达式
#grep结合正则表达式
#准备测试数据
$ vim test.txt
ni
hao
ma?
123
abc123
aBc123
aBC123
123abc
a
bb
ab
abb
abbb
abcde
ccc
bbbddd
ni hao ma?
ni hen hao
ni bu hao
bu bu hao
index.php
index2php
字符匹配
🎈1,匹配具体的字符
grep "abc" test.txt
🎈2,匹配在或不在某几个字符中的一个,[]
#匹配有a或s或d的字符
grep "[asd]" test.txt
#匹配有除a或s或d之外的字符
grep "[^asd]" test.txt
区别于:
grep -v [asd] test.txt
#[]含义是匹配中括号中任意一个字符,注意只能匹配一个字符
#[^]含义是匹配不含有中括号里任意一个字符的行,注意也是一个一个字符匹配
[0-9] [a-z] [A-Z] [a-Z]或[a-zA-Z] 类似这样的都可以,指定匹配的范围
🎈3,匹配任意一个字符
#匹配长度是3位的字符串,h开头、o结尾
grep "h.o" test.txt
位置匹配
^ 匹配行首
$ 匹配行尾
#匹配以b开头的行
egrep "^b" test.txt
#匹配不是以小写字母开头的行
grep "^[^a-z]" test.txt
#匹配以o结尾的行
grep "o$" test.txt
#匹配以.php结尾的行
grep "\.php" test.txt
#匹配以a开头、以3结尾的行
grep "^a.*3$" test.txt
#匹配空白行
grep "^$" test.txt
#匹配数字行
grep "^[0-9]+$" test.txt
次数匹配
所有匹配次数的都是针对它前一个字符而言的
#*是匹配前一个字符出现任意多次(0次或任意多次)
grep "a*" test.txt
#则a*代表过滤a出现任意次的,即零次或任意次数,即把所有行拿出来
grep "a*" test.txt | wc -l
wc -l test.txt
=>
* 表示前面字符出现0次或者多次------
? 表示前面字符出现0次或者1次
+ 表示前面字符出现1次或任意次
{n} 表示前面的字符恰好出现n次
{n,} 表示其前面的字符出现不小于n次
{n,m} 匹配其前面的字符出现不小于n次,最多出现m次
#扩展正则生效的写法
grep "b\?" test.txt
或:
grep -E "b?" test.txt
或:
egrep "b?" test.txt
#匹配合适的行
[root@localhost ~]# egrep "ab?" test.txt
[root@localhost ~]# egrep "ab*" test.txt
[root@localhost ~]# egrep "ab+" test.txt
#{n} 表示前面的字符恰好出现n次
[root@localhost ~]# egrep "b{2}" test.txt
bb
abb
abbb --看红色匹配到的部分
bbbddd --看红色匹配到的部分
#{n,} 表示其前面的字符出现不小于n次
[root@localhost ~]# egrep "b{2,}" test.txt
bb
abb
abbb --看红色匹配到的部分
bbbddd --看红色匹配到的部分
#{n,m} 匹配其前面的字符出现不小于n次,最多出现m次
[root@localhost ~]# egrep "b{1,2}" test.txt
abc123
123abc
bb
ab
abb
abbb
abcde
bbbddd --看红色匹配到的部分
ni bu hao
bu bu hao
[root@localhost ~]# egrep "^b{1,2}" test.txt
bb
bbbddd --看红色匹配到的部分
bu bu hao
分支和整体匹配
#分支匹配
#同时匹配ni hen hao和ni bu hao
[root@localhost ~]# egrep "ni hen|bu" test.txt
ni hen hao
ni bu hao
bu bu hao
[root@localhost ~]# egrep "ni hen|ni bu" test.txt
ni hen hao
ni bu hao
或:
[root@localhost ~]# egrep "ni (hen|bu)" test.txt
ni hen hao
ni bu hao
#可以匹配一个整体的个数,类似匹配字符个数
[root@localhost ~]# egrep "(bu)+" test.txt
ni bu hao
bu bu hao
正则表达式练习
写脚本:
- 输入用户名、密码
- 验证用户名格式:由4-8位的数字、字母、下划线组成,且数字不在开头
- 判用户是root、密码是123,打印绿色登录成功、否则打印红色登录失败
匹配ip地址:
- 在ip addr中匹配正在使用的网卡的ip
匹配电话号码:
- 以区号025开头
- 号码是5或8开头的八位数
- 区号和号码间可以是空格、-或没有
#测试数据
$ cat telephones.txt
02588888888
025-5555555555
025 12345678
025 54321678
025ABC88888
025-85432109
028-85643210
0251-52765421
#可以的正则表达式:
$ egrep "^(025)[ -]?[58][0-9]{7}$" telephones.txt
------
#手机号匹配需求:
长度11位,
第一位是1,
第二三位是:130、131、132、145、155、156、185、186,
剩余位:不限制
匹配邮箱
- 邮箱格式:用户名@二级域名.顶级域名
- 用户名:字符长度在5-17位,是除了@和空格以外的任意字符,开头只能是字母或者_
- 二级域名:长度不限,符号为数字、小写字母、中横线-(不能连续、不在行尾)
- 顶级域:域名后缀范围在.com、.cn、.com.cn中
#匹配邮箱:
$ cat mail.txt
zhangsan123@qq.com
li si@163.com
wang@wu@sina.com
zhao liu@126.com
qianqi@sina.com.cn
tester_ni@-sina.com.cn
wangwu@sina.2com.cn
_user1@@jd-1.com
#可以的正则表达式:
$ egrep "^[a-zA-Z_][^@ ]{4,16}@(-?[0-9a-z])+(\.com|\.cn|\.com\.cn)$" mail.txt
或:
$ egrep "^[a-zA-Z_][^@ ]{4,16}@(-[0-9a-z]|[0-9a-z])+(\.com|\.cn|\.com\.cn)$"
mail.txt
字符截取和替换命令
cut命令
功能:用于从文件或标准输入中提取指定字段或列
语法:cut [选项] 文件名
选项:
-f 列号:提取第几列,默认识别制表符分割出来的列
-d 分隔符:按照指定的分割符进行分割,然后结合-f提取指定列
-c 字符范围:不依赖分割符来分割,而是通过字符范围进行提取
n-m 表示从第n提取到第m个字符
n- 表示从第n个字符开始提取到结尾
-m 表示从第一个字符提取到第m个
#准备测试数据:添加以下内容,列之间用制表符分割(tab)
$ cat cut.txt
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
1 xqian 83 75 93
2 xsun 74 96 63
#-f选项 过滤指定列的内容(多列用逗号分割)
$ cut -f 2,5 cut.txt
NAME DOCKER
xzhao 78
xqian 93
xsun 63
#cut只能分割用制表符分开列的文件,若列是用其他符号分割的,需要用-d选项指定
#通过指定分割符的方式来确定如何进行分割,分开后就有列,截取第一个和第七个列
$ cut -d ":" -f 1,7 /etc/passwd
root:/bin/bash
bin:/sbin/nologin
daemon:/sbin/nologin
┊
#获取当前所在路径的最后一个层级目录
$ cd /home/jimmy
$ pwd | cut -d/ -f3
#-c 可以按照字符数量进行截取,按照对应格式可以取到对应位置的字符
#截取多个范围的字符时用,隔开
格式:[n-m]、[n-]、[-m]
$ head -5 anaconda-ks.cfg
# Generated by Anaconda 34.25.4.9
# Generated by pykickstart v3.32
# version=RHEL9
# Use graphical install
graphical
$ head -5 anaconda-ks.cfg | cut -c 1-5
# Gen
# Gen
#vers
# Use
graph
$ head -5 anaconda-ks.cfg | cut -c 1-5,10-15
# Gened by
# Gened by
#versRHEL9
# Usephical
graph
------------------------
#练习:使用cut将磁盘的使用率截取出来
$ df -h | cut -d"%" -f1 | cut -c 1-20,40-43
文件系统 容量 已用 # 中文字符和英文字符长度不一致
devtmpfs 4.0M
tmpfs 968M
tmpfs 388M
/dev/nvme0n1p3 15G
/dev/nvme0n1p1 960M
/dev/nvme0n3p1 40M
/dev/sr0 11G
tmpfs 194M
[!NOTE]
shell三剑客grep、awk、sed
awk命令
功能:awk是一种编程语言,用于在linux/unix中对文本和数据进行处理,awk既可以实现对文件的行提取,也可以实现对文件的列提取,之所以叫 awk 是因为其取了三位创始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符,awk是一个很全面的工具,此处我们只讲在Linux中如何利用awk进行字符串过滤,不讲过多关于awk编程的内容
格式:awk '条件{动作}' 文件名
1,条件的作用:通过指定条件过滤出符合条件的行,没有指定则是操作所有行
2,动作的作用:通过动作将符合条件的行打印出来,并且在打印时我们可以选择打印该行中的哪些列
#不指定任何条件,直接执行动作,并选择输出哪些列,常用printf来打印
$ cat cut.txt
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
1 xqian 83 75 93
2 xsun 74 96 63
$ cut -f 2,5 cut.txt
NAME DOCKER
xzhao 78
xqian 93
xsun 63
#等价cut,截取第2列和第5列的值
$ awk '{printf $2"\t"$5"\n"}' cut.txt
或:
$ awk '{print $2"\t"$5}' cut.txt
NAME DOCKER
xzhao 78
xqian 93
xsun 63
内置 | 作用 |
---|---|
$0 | 代表awk读入当前行的整行数据 |
$n | 代表awk读入当前行的第n列数据 |
NR | 代表当前awk正在处理的行的行号 |
NF | 代表当前awk读取数据总字段数(总列数) |
FS | 用来声明awk的分隔符,如BEGIN {FS=":"} |
#读取文件中任意一列的数据
$ awk '{printf $1"\t" $2"\t" $3"\n"}' cut.txt
$ awk '{printf $0"\n"}' cut.txt
#NF
#读取文件的每行的总列数
$ awk '{printf NF "\n"}' cut.txt
#获取文件的最后一列
$ awk '{printf $NF "\n"}' cut.txt
#获取文件的倒数第二列
$ awk '{printf $(NF-1) "\n"}' cut.txt
#读取文件的第一列和最后一列
$ awk '{printf $1"\t" $NF"\n"}' cut.txt
#FS或-F
#awk默认可以识别的分隔符:空白(即空格、tab、连续的空格、连续的tab)
#读取/etc/passwd中第一列和最后一列
$ awk 'BEGIN {FS=":"}{printf $1"\t"$NF"\n"}' /etc/passwd
或:
$ awk -F: '{printf $1"\t"$NF"\n"}' /etc/passwd
#获取当前所在路径的最后一个层级目录
$ cd /home/jimmy
$ pwd | awk -F/ '{printf $NF "\n"}'
#NR条件
#截取磁盘的使用率
$ df -h | awk '{printf $5"\n"}'
#截取磁盘使用率的数据部分
$ df -h | awk 'NR>1{printf $5"\n"}'
#截取根分区的磁盘使用率
$ df -h | awk 'NR==5{printf $5"\n"}'
#截取根分区的磁盘使用率的数值
$ df -h | awk 'NR==5{printf $5"\n"}' | cut -d% -f1
格式:awk '条件{动作}' 文件名
awk的条件
- 自定义条件
- 关系运算条件(>、<、>=、<=、==、!=),用来判断左右两侧的关系
- 包含匹配条件(~、!~、~//、!~//),用来进行匹配包含关系的
- 预定义条件(保留字)
- BEGIN:在awk未读取数据前声明的条件,该条件后的动作仅在程序开始时执行一次,不会重复执行
- END:类似于BEGIN,在awk处理完所有数据后声明的条件,在该条件后的程序仅在程序结束前执行一次
[!NOTE]
注:若有多个 条件{动作} 可以用空格分割
关系运算条件(>、<、>=、<=、==、!=),用来判断左右两侧的关系,一般左侧为变量,右侧为参考值
#列出行号大于1的行的所有列信息
$ awk 'NR>1{printf $0"\n"}' cut.txt
0 xzhao 95 59 78
1 xqian 83 75 93
2 xsun 74 96 63
#列出学号为2号的各科成绩单
$ awk '$1==2{printf $0"\n"}' cut.txt
2 xsun 74 96 63
#列出Linux成绩大于等于80分的成绩单
$ awk '$3>=80{printf $2"\t"$3"\n"}' cut.txt
NAME LINUX
xzhao 95
xqian 83
$ awk '$3==83{printf $2"\t"$3"\n"}' cut.txt
xqian 83
$ awk '$3<=83{printf $2"\t"$3"\n"}' cut.txt
xqian 83
xsun 74
#第一行表头只会出现在大于号的结果中
#获取当前用户使用的网卡名
$ ip addr | grep 'inet ' | grep -v '127.0.0.1' | awk 'NR==1{printf $NF "\n"}'
$ ip addr | awk '/ens/{print $0}' | grep -v ":" | awk '{print $NF}'
[!NOTE]
包含匹配条件(~、!~、~//、!~//),用来进行匹配包含关系的,判断左侧变量中是否包含右侧的字符串,当右侧字符串中包含一些特殊符号时,需要使用//然后在里面使用\转义符将符号转义为普通字符
> #列出第二列包含字符串q的数据
> $ awk '$2~"q"{printf $0"\n"}' cut.txt
> 1 xqian 83 75 93
> #列出名字以n结尾的数据
> $ awk '$2~"n$"{printf $0"\n"}' cut.txt
> $ awk '$2~/n$/{printf $0"\n"}' cut.txt
> 1 xqian 83 75 93
> 2 xsun 74 96 63
> #列出包含数字3的数据
> $ awk '$0~"3"{printf $0"\n"}' cut.txt
> $ awk '$0~/3/{printf $0"\n"}' cut.txt
> $ awk '/3/{printf $0"\n"}' cut.txt
> 1 xqian 83 75 e93
> 2 xsun 74 96 63
> #列出包含以.com结尾的数据
> $ awk '/\.com$/{printf $0"\n"}' cut.txt
> #列出指定设备的磁盘使用率
> $ df -h | awk '/(sd|sr)[a-z]?[0-9]/{printf $1"\t"$5"\n"}'
> /dev/sr0 100%
> /dev/sda1 30%
> #列出包含xsun的数据
> $ awk '$2~"xsun"{printf $0"\n"}' cut.txt
> $ awk '/xsun/{printf $0"\n"}' cut.txt
> 2 xsun 74 96 63
> #使用外部变量
> $ name=xsun
> $ awk '/'$name'/{printf $0"\n"}' cut.txt
> 或:
> $ awk '/'''$name'''/{printf $0"\n"}' cut.txt
> 2 xsun 74 96 63
#预定义条件:BEGIN、END
#在处理数据前执行BEGIN动作,若有多个 条件{动作} 可以用空格分割
$ awk 'BEGIN{printf "MYSQL成绩单:\n"} {printf $2"\t"$4"\n"}' cut.txt
MYSQL成绩单:
NAME MYSQL
xzhao 59
xqian 75
xsun 96
#在操作完数据后执行END动作
$ awk '{printf $2"\t"$5"\n"}END{printf "以上显示的是所有人的DOCKER成绩\n"}' cut.txt
NAME DOCKER
xzhao 78
xqian 93
xsun 63
以上显示的是所有人的DOCKER成绩
[!IMPORTANT]
awk的工作原理
- 先查看是否有BEGIN条件,有则先执行BEGIN后面的动作;
- 然后读入第一行数据,使用分隔符分隔好列之后依次赋值给变量$0、$1、$2、$3 ...等,$0代表整行数据,$1为第一列数据,依次类推。第一行将所有内容赋值完成后,进行条件判断,按照符合条件的动作执行,不满足不执行;
- 处理完第一行之后,将第二行数据重复第一行的所有步骤,依次处理每行数据直到处理完整个文本
- 最后看是否有END条件,有则执行一次END后面的动作;
动作{printf}
功能:printf是标准的格式化输出,取消所有默认格式,然后手动指定输出内容的类型和输出时的格式
格式:printf '类型/格式' 字符串
输出类型:
%s 将内容按照字符串类型输出,默认类型
(如:ns:代表输出宽度是n,默认右对齐,-ns左对齐)
%i 将内容按照整数类型输出(=%d)
(如:ni:代表输出宽度是n,默认右对齐,-ns左对齐)
%f 将内容按浮点数类型输出
(如:%.2f:代表输出小数点数值时保留两位小数点,会进行四舍五入)
输出格式:
\t:字符之间用制表符分割,即tab键
\n:字符之间用换行符分割,即enter键
注:输出格式需要加双引号
#使用printf输出表格文件
$ cat cut.txt
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
1 xqian 83 75 93
2 xsun 74 96 63
$ printf '%s' $(cat cut.txt)
IDNAMELINUXMYSQLDOCKER0xzhao9559781xqian8375932xsun749663[root@localhost ~]#
#在使用printf输出时,如果仅指定输出类型,而不指定输出格式,则会把所有要输出内容连在一起输出,变为一整行
$ printf '%s\t%s\t%s\t%s\t%s\n' $(cat cut.txt)
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
1 xqian 83 75 93
2 xsun 74 96 63
#在输出时,想让第1列使用整数类型输出,3,4,5列使用浮点类型输出
$ printf '%i\t%s\t%.2f\t%.2f\t%.2f\n' $(cat cut.txt)
-bash: printf: ID: invalid number
-bash: printf: LINUX: invalid number
-bash: printf: MYSQL: invalid number
-bash: printf: DOCKER: invalid number
0 NAME 0.00 0.00 0.00
0 xzhao 95.00 59.00 78.00
1 xqian 83.00 75.00 93.00
2 xsun 74.00 96.00 63.00
#取消对表格首行的指定格式输出
$ printf '%i\t%s\t%.2f\t%.2f\t%.2f\n' $(cat cut.txt|grep -v ID)
0 xzhao 95.00 59.00 78.00
1 xqian 83.00 75.00 93.00
2 xsun 74.00 96.00 63.00
#更改cut内容,变成小数
$ cat cut.txt
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
1 xqian 83 75 93
2 xsun 59.996 96 63
#打印名字和Linux成绩,且成绩保留2位小数
$ printf '%s\t%.2f\n' $(awk 'NR>1{printf $2"\t"$3"\n"}' cut.txt)
xzhao 95.00
xqian 83.00
xsun 60.00
#指定数字的输出长度及对齐方式
$ printf '%-20i\t%20i\n' $(awk 'NR>1{printf $4"\t"$5"\n"}' cut.txt)
#指定字符串输出长度及对齐方式
$ printf '%-23s\t%4s\n' $(df -h | awk '{printf $1"\t"$5"\n"}')
----
#awk中默认支持数值运算,并且整数、浮点数运算都支持
#计算每个人的平均值
$ awk 'NR>1{printf $2"的平均分是\t"($3+$4+$5)/3"\n"}' cut.txt
xcang的平均分是 77.3333
xbo的平均分是 83.6667
xlong的平均分是 77.6667
#浮点型截取指定位数
$ printf '%s\t%.2f\n' $(awk 'NR>1{printf $2"的平均分是\t"($3+$4+$5)/3"\n"}'
cut.txt)
xcang的平均分是 77.33
xbo的平均分是 83.67
xlong的平均分是 77.67
sed命令
sed是一种(stream editor)流编辑器,适合对文件内容进行简单的替换、删除等操作
功能:实现非交互式对文件数据进行选取、替换、删除、新增等操作的命令,即不进入文本内对其内容进行修
改;主要包括读取、执行和显示三个过程
格式:sed [选项] '动作' 文件名
选项:
-n 将经过处理后的数据输出到控制台上;不加-n输出全文+指定行
-i 直接修改文件内容;默认下sed不会对文件直接进行修改,而是在内存中修改并将结果效果显示在控制台上
动作:
p print 打印,输出指定的行
a append 追加,在当前行后追加一行或多行
i insert 插入,在当前行前插入一行或多行
d delete 删除,删除指定的一行或多行
c character 整行替换,用c后面的字符串替换原数据指定行的数据
s string 字串替换,用一个字符串替换另外一个字符串,格式“行范围s/旧字串/新字串/g”
----数据准备
$ cat cut.txt
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
1 xqian 83 75 93
2 xsun 74 96 63
-----查看
#显示cut.txt中第三行的信息:
$ sed '3p' cut.txt
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
1 xqian 83 75 93 --
1 xqian 83 75 93 --
2 xsun 74 96 63
$ sed -n '2p' cut.txt
1 xqian 83 75 93
#查看连续的多行
$ sed -n '2,4p' cut.txt
0 xzhao 95 59 78
1 xqian 83 75 93
2 xsun 74 96 63
#查看不连续的多行
$ sed -n '2p;4p' cut.txt
0 xzhao 95 59 78
2 xsun 74 96 63
#查看符合条件的行,支持正则,扩展正则需要加转义符
$ sed -n '/a/p' cut.txt
0 xzhao 95 59 78
1 xqian 83 75 93
$ sed -n '/[23]/p' cut.txt
1 xqian 83 75 93
2 xsun 74 96 63
$ sed -n '/[23]\?/p' cut.txt
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
1 xqian 83 75 93
2 xsun 74 96 63
----追加、不加-i不改变源数据
#a在指定行后面追加
$ sed '3a 12 xli 80 90 100' cut.txt
$ sed '3a 12 -添加行的数据间用空格
xli 80 90 100' cut.txt -添加行的数据间用tab
$ sed '3a 12\txli\t80\t90\t100\t' cut.txt -添加行的数据间用\t
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
1 xqian 83 75 93
12 xli 80 90 100
2 xsun 74 96 63
#i在指定行前面插入
$ sed '3i 12\txli\t80\t90\t100\t' cut.txt
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
12 xli 80 90 100
1 xqian 83 75 93
2 xsun 74 96 63
#-a和-i可以在指定位置上追加多行
$ sed '3a 100 zhangsan \n200 lisi' cut.txt
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
1 xqian 83 75 93
100 zhangsan
200 lisi
2 xsun 74 96 63
#加上-i选项才能更改源数据
$ sed -i '3i 12\txli\t80\t90\t100\t' cut.txt
$ cat cut.txt
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
12 xli 80 90 100
1 xqian 83 75 93
2 xsun 74 96 63
----删除、不加-i不改变源数据
#删除cut.txt中的第3行数据
$ sed '3d' cut.txt
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
1 xqian 83 75 93
2 xsun 74 96 63
#删除cut.txt中的第2行到第4行数据
$ sed '2,4d' cut.txt
ID NAME LINUX MYSQL DOCKER
#删除cut.txt中的第2行和第4行数据
$ sed '2d;4d' cut.txt
ID NAME LINUX MYSQL DOCKER
1 xqian 83 75 93
$ cat cut.txt
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
1 xqian 83 75 93
2 xsun 74 96 63--
#加上-i选项才能更改源数据
$ sed -i '3d' cut.txt
$ cat cut.txt
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
1 xqian 83 75 93
2 xsun 74 96 63
----替换、不加-i不改变源数据
#整行替换数据:将cut.txt中第3行内容换成'No such person'
$ sed '3c No such person' cut.txt
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
No such person
2 xsun 74 96 63
#字符串替换:将cut.txt中第3行的'x'换成'xiao'
$ sed '3s/x/xiao/g' cut.txt
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
1 xiaoqian 83 75 93
2 xsun 74 96 63
#字符串替换:将cut.txt中所有行的'x'换成'xiao'
$ sed 's/x/xiao/g' cut.txt
ID NAME LINUX MYSQL DOCKER
0 xiaozhao 95 59 78
1 xiaoqian 83 75 93
2 xiaosun 74 96 63
#字符串替换:将指定的一行内容替换成空
$ sed '3s/[0-9]//g' cut.txt
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
xqian
2 sun 74 96 63
#字符串替换:同时替换多行,用;分隔
$ sed '3s/^/#/g;4s/[0-9]//g' cut.txt
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
#1 xqian 83 75 93
xsun
$ cat cut.txt
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
1 xqian 83 75 93
2 xsun 74 96 63
------------
#加上-i选项才能更改源数据
$ sed -i '3s/^/#/g;4s/[0-9]//g' cut.txt
$ cat cut.txt
ID NAME LINUX MYSQL DOCKER
0 xzhao 95 59 78
#1 xqian 83 75 93
xsun
----
#练习:想关掉SELiunx,用sed命令更改配置文件
$ sed -i '22s/disabled/enforcing/g' /etc/selinux/config
#练习:用sed命令修改网卡配置文件,进行IP地址获取方式的切换
#获取当前使用的网卡名
$ interface=$(ip addr | grep -e 'inet ' | grep -v '127.0.0.1' | awk 'NR==1{printf $NF "\n"}')
#将较长的配置文件名赋予变量
$ cf="/etc/NetworkManager/system-connections/$interface.nmconnection"
#查看当前网卡配置,并显示行号
$ cat -n $cf
1 [connection]
2 id=ens160
3 uuid=942f40a1-09af-347e-bc1e-6ad72c53b39f
4 type=ethernet
5 autoconnect-priority=-999
6 interface-name=ens160
7 timestamp=1733853830
8
9 [ethernet]
10
11 [ipv4]
12 method=manual
13 address=192.168.66.191/24,192.168.66.195
14
15 [ipv6]
16 addr-gen-mode=eui64
17 method=auto
18
19 [proxy]
#将静态配IP改为dhcp获取
$ sed '12s/manual/auto/g;13s/^/#/g' $cf
[!TIP]
tr命令
#替换大小写: $ echo "test 123" | tr 'a-z' 'A-Z' #删除空格: $ echo "a ff xx 123 " | tr -d ' ' 或: $ echo "a ff xx 123 " | sed 's/ //g' tr --help 用法:tr [选项]... SET1 [SET2] Translate, squeeze, and/or delete characters from standard input, writing to standard output. -c, -C, --complement use the complement of SET1 -d, --delete delete characters in SET1, do not translate -s, --squeeze-repeats replace each sequence of a repeated character that is listed in the last specified SET, with a single occurrence of that character -t, --truncate-set1 first truncate SET1 to length of SET2 --help 显示此帮助信息并退出 --version 显示版本信息并退出 SET 是一组字符串,一般都可按照字面含义理解。解析序列如下: \\ 反斜杠 \a 终端鸣响 \b 退格 \f 换页 \n 换行 \r 回车 \t 水平制表符 \v 垂直制表符 字符1-字符2 从字符1 到字符2 的升序递增过程中经历的所有字符 [字符*] 在SET2 中适用,指定字符会被连续复制直到吻合设置1 的长度 [字符*次数] 对字符执行指定次数的复制,若次数以 0 开头则被视为八进制数 [:alnum:] 所有的字母和数字 [:alpha:] 所有的字母 [:lower:] 所有的小写字母 [:upper:] 所有的大写字母 [:digit:] 所有的数字 [:graph:] 所有的可打印字符,不包括空格 [:print:] 所有的可打印字符,包括空格 [:punct:] 所有的标点字符 [=字符=] 所有和指定字符相等的字符 #拓展# 该命令将从 /dev/urandom 设备读取随机数据,并使用 tr 命令过滤和截取所需长度的字符 pw=`tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 10` 'A-Za-z0-9' == [:alnum:] 随机生成10位字符包含大小写字母和数字
sort命令
功能:对指定文件里的行进行排序,默认使用每行开头第一个字符进行排序
语法:sort [选项] 文件名
选项:
-f 忽略大小写
-b 忽略每行前的空白部分
-n 以数值型进行排序,默认使用字符串类型排序
-r 反向排序
-u 删除重复行(=下面的uniq)
-t 指定分隔符,默认分割符是制表符
-k n 指定使用第几列的内容进行排序,一般和-t结合使用
#测试数据
$ cat sort.txt
Cdm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
root:x:0:0:root:/root:/bin/bash
#忽略大小写
$ sort -f sort.txt
daemon:x:2:2:daemon:/sbin:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
Cdm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
root:x:0:0:root:/root:/bin/bash
#忽略大小写和行前空白
$ sort -bf sort.txt
bin:x:1:1:bin:/bin:/sbin/nologin
Cdm:x:3:4:adm:/var/adm:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
root:x:0:0:root:/root:/bin/bash
#去掉重复行
$ sort -bfu sort.txt
bin:x:1:1:bin:/bin:/sbin/nologin
Cdm:x:3:4:adm:/var/adm:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
root:x:0:0:root:/root:/bin/bash
#冒号分隔符,第三列,从小到大
$ sort -t: -k 3 -n sort.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
Cdm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
#以数值方式反向排序,从大到小
$ sort -t: -k 3 -nru sort.txt
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
Cdm:x:3:4:adm:/var/adm:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
root:x:0:0:root:/root:/bin/bash
uniq命令
功能:用来取消重复行,与sort -u 功能是一样的
格式:uniq [选项] 文件名
选项:
-i 忽略大小写
-c 在关键词旁显示该关键词出现的次数(一般针对行)
[!CAUTION]
注意:当重复行不连续时,uniq是不生效的,需要先排序,再执行
#准备数据
$ cat uniq.txt
linux 30
unix 20
windows 50
windows 50
Windows 50
Linux 30
windows 50
#去重
$ uniq uniq.txt
linux 30
unix 20
windows 50
Windows 50
Linux 30
windows 50
#比原来少了一样,只去掉了相邻重复的一行
$ uniq uniq.txt |wc -l
6
#想去除所有重复行,需要先排序
$ sort uniq.txt | uniq
linux 30
Linux 30
unix 20
windows 50
Windows 50
$ sort uniq.txt | uniq -c
1 linux 30
1 Linux 30
1 unix 20
3 windows 50
1 Windows 50
$ sort uniq.txt | uniq -ci | sort -rn
4 windows 50
2 linux 30
1 unix 20--------
#获取当前系统用户的shell类型、并分类统计、按降序排序
$ cut -d: -f7 /etc/passwd | sort | uniq -c | sort -nr
条件判断
按照文件类型进行判断
测试选项 | 作用 |
---|---|
-b 文件 | 判断该文件是否存在、且为块设备文件 |
-c 文件 | 判断该文件是否存在、且为字符设备文件 |
-d 文件 | 判断该文件是否存在、且为目录 |
-e 文件 | 判断该文件是否存在 |
-f 文件 | 判断该文件是否存在、且为普通文件 |
-L 文件 | 判断该文件是否存在、且为符号链接文件 |
-p 文件 | 判断该文件是否存在、且为管道文件 |
-s 文件 | 判断该文件是否存在、且内容非空(有内容为真) |
-S 文件 | 判断该文件是否存在、且为套接字文件 |
#使用[]或test进行判断,使用[]时与内容左右都需要有一个空格
#可以使用echo $?来检测判断的结果,0为真、1为假
[ -e /etc/passwd ]
echo $? -- 0
test -e /etc/new
echo $? -- 1
#也可以结合 && 和 || 来测试。
[ -e /etc/passwd ] && echo yes || echo no
[ -e /etc/new ] && echo yes || echo no
----------------
#判断是文件还是目录
[ -f /root ] && echo yes || echo no
[ -d /root ] && echo yes || echo no
#判断文件是否为空
touch test.txt
[ -s test.txt ] && echo yes || echo no
echo "test data" > test.txt
[ -s test.txt ] && echo yes || echo no
#是软链接文件
[ -L /etc/rc.local ] && echo yes || echo no
cd /dev
#块设备是I/O设备中的一类,是将信息存储在固定大小的块中,每个块都有自己的地址,还可以在设备的任意
位置读取一定长度的数据,例如U盘,SD卡
[ -b /dev/sr0 ] && echo yes || echo no
[ -b /dev/nvme0n1p1 ] && echo yes || echo no
#字符设备是指在I/O传输过程中以字符为单位进行传输的设备例如键盘,打印机
[ -c /dev/stdin ] && echo yes || echo no
[ -c /dev/stdout ] && echo yes || echo no
[ -c /dev/zero ] && echo yes || echo no
[ -c /dev/null ] && echo yes || echo no
cd /run
#是套接字文件
[ -S /run/mcelog-client ] && echo yes || echo no
#是管道文件
[ -p /run/initctl ] && echo yes || echo no
--------------------------
=>同系统命令ll(ls -l)-:普通文件
d:目录文件
b:块设备文件
c:字符设备文件
l:链接文件
p:管道文件套接字文件
s:套接字文件
----------
#上面所有的选项都是判2个条件,文件存在且为什么类型
即:判断一个文件是不是普通文件,赋值不同得到结果不同
$ filename=/etc/passwd
$ [ -f "$filename" ] && echo "是普通文件" || ( [ -e "$filename" ] && echo "不是普通
文件" || echo "不存在" )
#多层条件判断时结构复杂,可以换if结构进行判断
字符串的判断
测试项目 | 作用 |
---|---|
-z 字符串 | 判断字符串是否为空(为空返回TRUE) |
-n 字符串 | 判断字符串是否为非空(非空返回TRUE) |
字符串1 == 字符串2 | 判断字符串1是否和字符串2相等(相等返回真) |
字符串1 != 字符串2 | 判断字符串1是否和字符串2不相等(不相等返回真) |
#一个字符串进行判断时
name=jimmy
age=""
unset sex
[ -z "$name" ] && echo yes || echo no
[ -z "$age" ] && echo yes || echo no
[ -z "$sex" ] && echo yes || echo no
=> 字符串没有定义和没有赋值结果都为空----
#多个字符串进行比较时:
name=jimmy
age=""--no--yes --yes
[ "$name" == $age ] && echo yes || echo no-bash: [: jimmy:需要一元表达式
#每个变量要加引号,不然没定义或没赋值时报语法错
[ "$name" == "$age" ] && echo yes || echo no --yes
或:
[[ "$name" == $age ]] && echo yes || echo no
=>[]和[[]]的区别:
1、[[]]会识别比较符号,若是二元比较符,即使一个值没定义或没赋值,也会自动转为空字符串;
2、[[]]支持的表达式比[]更多,如:
在数值比较时支持>、<等符号;
有符号=~判断包含,参见cat .bashrc
name=jimmy;[[ $name =~ "y" ]] && echo yes || echo no
按照文件权限进行判断
测试选项 | 作用 |
---|---|
-r 文件 | 判断该文件是否存在、且拥有读权限 |
-w 文件 | 判断该文件是否存在、且拥有写权限 |
-x 文件 | 判断该文件是否存在、且拥有执行权限 |
-u 文件 | 判断该文件是否存在、且拥有SUID权限 |
-g 文件 | 判断该文件是否存在、且拥有SGID权限 |
-k 文件 | 判断该文件是否存在、且拥有SBit权 |
ll -d /etc/passwd
=>-rw-r--r--
[ -r /etc/passwd ] && echo yes || echo no
[ -w /etc/passwd ] && echo yes || echo no
[ -x /etc/passwd ] && echo yes || echo no
ll -d /usr/bin/passwd --跟当前登录人权限有关
=>-rwsr-xr-x.
[ -u /usr/bin/passwd ] && echo yes || echo no --yes
[ -g /usr/bin/passwd ] && echo yes || echo no
[ -k /usr/bin/passwd ] && echo yes || echo no
#特殊权限的文件
#含有SUID权限,即当用户执行文件时会以用户所有者的身份进行执行
ll -d /usr/bin/passwd =>-rwsr-xr-x. u+s
#含有SUID权限,即当用户执行文件时会以用户所有者的身份进行执行
ll -d /usr/bin/locate =>-rwx--s--x. g+s
#含有SBIT权限,即设置该权限的目录,只有文件的创建者、目录所有者和root可以删除自己文件
ll -d /tmp =>drwxrwxrwt. O+t
两个文件间进行比较
测试项目 | 作用 |
---|---|
文件1 -nt 文件2 | 判断文件1的修改时间是否比文件2的新 |
文件1 -ot 文件2 | 判断文件1的修改时间是否比文件2的旧 |
文件1 -ef 文件2 | 判断文件1是否和文件2的Inode号一致, 即判两个文件是否为同一个文件(常用于判断硬链接) |
#判断是否为硬链接
cd
echo "ying test" > ying.txt
ln ying.txt /tmp/
[ /root/ying.txt -ef /tmp/ying.txt ] && echo yes || echo no
两个整数之间的比较
测试选项 | 作用 |
---|---|
整数1 -eq 整数2 | 判断整数1是否和整数2相等(equal ) |
整数1 -gt 整数2 | 判断整数1是否大于整数2(greater than) |
整数1 -lt 整数2 | 判断整数1是否小于整数2(less than) |
整数1 -ge 整数2 | 判断整数1是否大于等于整数2(greater than or equal to) |
整数1 -le 整数2 | 判断整数1是否小于等于整数2(less than or equal to) |
[ 11 -ge 22 ] && echo yes || echo no
a=123
b=124
[ $a -eq $b ] && echo yes || echo no
[ $a -gt $b ] && echo yes || echo no
[ $a -lt $b ] && echo yes || echo no
多重条件判断
测试选项 | 作用 |
---|---|
判断1 -a 判断2 | 逻辑与,判断1和判断2都成立,最终结果才为真 |
判断1 -o 判断2 | 逻辑非,判断1和判断2有一个成立,最终结果就为真 |
!判断 | 逻辑非,对判断结果取反,! 和 判断条件之间有个空格 |
#逻辑与
a=100
[ -n "$a" -a "$a" -gt 150 ] && echo yes || echo no
#逻辑或
b=150
[ "$b" -gt 150 -o "$b" -eq 150 ] && echo yes || echo no
#逻辑非
c=200
[ ! -n "$c" ] && echo yes || echo no
[!CAUTION]
\#[[]]和[]区别:
1、若进行字符串比较,使用到的变量可以不用写双引号,会自动识别变量个数
2、若进行数值比较,在[]支持的表达式(如:-gt)外,还支持>、<、==、!=等
3、若进行逻辑关系比较,与[]不同(-a、-o、!),而是(&&、||、 !)
4、进行字符串比较时,可进行模式匹配,支持通配符(如:[[ 3a == 3* ]])
可进行包含比较,支持正则(如:[[ "nihao" =~ "ni" ]])
5、其他表达式含义同,如:文件类型判断、文件权限判断、多文件判断等
流程控制
编程中流程控制分为三类:顺序结构、分支结构、循环结构
if条件判断
单分支if条件语句
- 单分支条件语句比较简单,只需一个判断条件,符合则执行,不符合则直接退出。
判断条件符合的情况:
- true
- test或中括号条件判断式的结果为0的
- 命令执行的结果为0的
格式:
- if [ 条件判断式 ]
then
- 程序
- fi
或:
if [ 条件判断式 ];then
- 程序
- fi
#监控根分区的使用率:
$ cat check_disk_usage.sh
#!/bin/bash
#设置阈值,即实际使用超过这个值进行报警
threshold=70
#获取df -h中以/结尾的设备(根分区)
rate=$(df -h | awk '/\/$/{printf $5"\n"}'|cut -d% -f1)
err_msg="Warning! root usered rate is >= $threshold; used:$rate%"
if [ $rate -gt $threshold ]
then
#若超过阈值则打印,并红色字体醒目显示
echo -e "\e[31m Warning! $err_msg \e[0m"
#安装邮件软件包
dnf -y install postfix s-nail
#或者发邮件,mail -s 邮件标题 用户名 < 包含文件内容的文件
echo $err_msg | mail -s "Disk Usage Warning" root
fi
----------------------------------
#监控内存使用率
$ free
total
used
Mem:
Swap:
1863252
2097148
164208
0
free
1004808
2097148
shared buff/cache available
10184
694236
#获取使用率,小数的
$ free | awk '/Mem/{printf $3/$2*100 "\n"}'
#对结果取整
$ printf '%.f\n' $(free | awk '/Mem/{printf $3/$2*100 "\n"}')
或:
$ free | awk '/Mem/{printf "%.f \n",$3/$2*100 }'
1490260
----------
#监控CPU使用率
$ top
$ top -n 1
#结果含义
第一行:当前时间;系统持续运行时间(up);登录用户数量;系统平均负载(一分钟、五分钟、十五分钟)
第二行:Tasks,进程数量的统计(总数、正在运行、正在睡眠、停止状态、僵死状态)
第三行:CPU使用情况
us:用户执行的进程所占用的CPU资源的百分比(用户占用)
sy:内核本身运行时所占用的CPU资源的百分比(系统占用)
ni:修改过进程优先级的进程,所占用的CPU资源百分比(us的占比包括ni占比)
id:CPU处于空闲状态的百分比
第四行:物理内存使用情况
total:总内存量
free:完全空闲的内存(所有空闲的内存的总和)
used:真正被利用和使用了的内存
buff/cache:被使用的内存当中,有一部分内存做了缓存(缓存区)
第五行:虚拟内存使用情况
#获取CPU使用率,仍需要小数变整数、进而进行阈值比较
$ top -n1 | grep "Cpu" | awk '{print $2+$4}'
双分支if条件语句
if [ 条件判断式 ]
then
条件成立时,执行的程序
else
条件不成立时,执行的另一个程序
fi
#判断文件是否存在
#!/bin/bash
#接受客户端输入一个文件名
read -p "Please input a file:" FILE
#检测是否有FILE文件
if [ -e $FILE ]
then
#有文件输出exists
echo "$FILE exists"
else
#没有文件输出not exists
echo "$FILE not exists"
fi
--------------------------
#监控服务,监控另一台机器上的httpd服务的状态
#nmap
功能:端口扫描命令,结果显示的端口一定是存活的
格式:nmap -sT 域名或IP
选项:
-s 扫描
-T 扫描所有开启的TCP端口
#脚本运行可以安装 yum -y install nmap httpd
#在本机上
$ dnf -y install nmap
#在被监控的机器上
$ dnf -y install httpd
$ systemctl restart httpd
----
$ vim jiankong_httpd.sh
#!/bin/bash
kai=$(nmap -sT 192.168.66.192 | grep "http$" )
if [ -n "$kai" ]
then
echo -e "\e[32m $(date) httpd is ok! \e[0m"
else
echo -e "\e[31m $(date) httpd is down!! \e[0m"
#远程重启需要学习了SSH服务才能完成
fi
多分支if条件语句
if [条件判断式1]
then
当条件判断式1成立时,执行程序1
elif [条件判断式2]
then
当条件判断式2成立时,执行程序2
......(可加入更多elif条件)
else
当所有条件不成立时,最后执行此程序
fi
#需求:判断一个文件,是否有输入,是否存在
#判断用户输入的文件的属性
#!/bin/bash
#接收键盘输入并赋予变量file
read -p "Please input you filename:" file
#判断file变量是否为空
if [ -z "$file" ]
then
echo "Error,please input a filename"
#判断file的值是否存在
elif [ ! -e "$file" ]
then
echo "your input is not exist"
#判断file的值是否为普通文件
elif [ -f "$file" ]
then
echo "$file is a regulare file"
#判断file是否是目录文件
elif [ -d "$file" ]
then
echo "$file is a directory!"
#如果都不是则执行本程序。
else
echo "$file is an other file!"
fi
多分支case条件语句
语法:
case $变量名 in
“值1”)
如果$变量等于值1,则执行程序1
;;
“值2”)
如果$变量等于值2,则执行程序2
;;
....省略...
*)
如果$变量的值不是以上值,则执行此程序
;;
esac
#只能进行一种条件的判断,会列出所有可能的值;
#case列出的值,可以是一个数字、一个字符串,或是一个简单的正则表达式--------------
#练习:写一个源码apache的启动管理脚本
#!/bin/bash
read -t 5 -p "请输入你的操作:" x
case "$x" in
start)
echo "正在启动服务。。"
;;
stop)
echo "正在停止服务。。"
;;
restart)
echo "正在重启服务。。"
;;
*)
echo "输入错误,请输入start|stop|restart"
;;
esac
--------------
#用户输入yes或no
#!/bin/bash
read -p "please enter choose: (yes|no)" choose
case "$choose" in
[yY]|[yY][eE][sS])
echo "执行"
;;
[nN]|[nN][oO])
echo "不执行"
;;
*)
echo "buzhidao"
esac
--------
#输入分数判断等级
#!/bin/bash
read -p "please enter your score: " score
case $score in
100|9[0-9]|8[5-9])
echo "优"
;;
7[5-9]|8[0-5])
echo "良"
;;
7[0-4])
echo "好"
;;
*)
echo "不及格"
;;
esac
----
#判断输入是否为数字
$ echo $input | sed 's/[0-9]//g'
$ [ -z "$guo" ] && echo "是数字" || echo "不是数字"
for循环
语法一:
for 变量 in 值1 值2 值3 ...
do
执行程序
done
语法二:
for ((初始值;循环控制条件;变量变化))
do
执行程序
done
[!CAUTION]
注:初始值:在循环开始时,需要给某个变量赋予初始值,如i=1;循环控制条件:用于指定变量循环的次数,如i<=100,则只要i的值小于等于100,循环就会继续;变量变化:每次循环之后,变量该如何变化,如i=i+1。代表每次循环之后,变量i的值都加1。
#!/bin/bash
#for的取值范围的几种写法
#for i in 1 2 3 4 5 6 7 8 9 10
#for i in {1..10}
#for i in {10..1}
#for i in {1..10..2}
#for i in {10..2..2}
#for i in $(seq 10)
#for i in $(seq 1 2 10)
for i in $(ls $PWD)
do
echo $i
done
------------
#类C结构的for循环
#!/bin/bash
sum=0
for ((i=1; i<=100; i=i+1))
do
sum=$(($sum+$i))
#或:let sum+=$i
done
echo "The sum of 1+2+....+100 is :$sum"
--------------
#自动解压
#!/bin/bash
pos=/lamp
for i in $(ls $pos/*.tar.gz)
do
echo $i
tar -xf $i -C $pos
done
----------------------------
#批量创建用户(默认用户名、默认密码)
#!/bin/bash
read -p "Please input user name:" -t 30 name
read -p "Please input the number of users:" -t 30 num
read -p "Please input the password of users:" -t 30 pass
if [ ! -z "$name" -a ! -z "$num" -a ! -z "$pass" ]
then
y=$(echo $num | sed 's/[0-9]//g')
if [ -z "$y"]
then
for (( i=1;i<=$num;i=i+1))
do
/usr/sbin/useradd $name$i &> /dev/null
echo $pass | /usr/bin/passwd --stdin $name$i &> /dev/null
done
fi
fi
while循环
只要条件判断式成立,循环就会一直继续,直到条件不成立,循环才停止
语法:
while [ 条件判断式 ]
do
程序
done
#练习:计算1+2+...+100的值
#!/bin/bash
i=1
sum=0
while [ $i -le 100 ]
do
sum=$(( $sum+$i))
i=$(( $i+1))
done
echo "the sum is:$sum"
----------------
#while true进行无限(死)循环:
#!/bin/bash
while true
do
kai=$(nmap -sT 192.168.66.192 | grep "http$" )
if [ -n "$kai" ]
then
echo -e "\e[32m $(date) httpd is ok! \e[0m"
else
echo -e "\e[31m $(date) httpd is down!! \e[0m"
#远程重启需要学习了SSH服务才能完成
fi
#检测的间隔时间为10秒
sleep 10
done
until循环
until循环和while循环相反,只要条件判断式不成立,则一直循环;直到条件成立则结束循环
格式:
until [ 条件判断式 ]
do
程序
done
#!/bin/bash
i=1
sum=0
until [ $i -gt 100 ]
do
let sum+=$i
let i++
done
echo "the sum is:$sum"
特殊流程控制
exit
系统中的exit是退出当前登录,但在shell中是退出脚本,回到Linux命令行
- 格式:exit [ 值 ]
- exit 退出时如果定义好了返回值,那么我们可以通过“$?”来查看。如果exit命令之后定义了返回值,那么这个脚本执行之后的返回值就是我们自己定义的返回值。可以通过$?查询,来查看返回值,范围是0-255。如果exit之后没有定义返回值,脚本执行之后的返回值是执行exit之前,最后执行一条命令的返回值
$ cat exit.sh
#判断用户输入的文件是否存在
#!/bin/bash
read -p "please enter a filename:" file
if [ -e "$file" ]
then
echo "文件存在"
else
echo "文件不存在"
#定义异常退出码
exit 8
fi
echo "byebye"
----------
#调用之后可以用$?查看结果
#文件存在,打印存在,并执行if之后的语句
#echo $?的结果是0
[root@localhost ~]# ./exit.sh
nihao
please enter a filename:/etc/passwd
文件存在
byebye
[root@localhost ~]# echo $?
0
#文件不存在,打印不存在,并退出脚本、不执行if之后的语句;
#echo $?的结果是自定义的8
[root@localhost ~]# ./exit.sh
nihao
please enter a filename:/etc/haha
文件不存在
[root@localhost ~]# echo $?
8
break
- break概述:跳出当前整个循环或结束当前循环,在for、while等循环语句中,用于跳出当前所在 的循环体,执行循环体之后的语句;
- 后面如果什么也不加,表示跳出当前循环等价于break 1,也可以在后面加数字,假设break 3表示 跳出第三层循环
#!/bin/bash
for ((i=1; i<=10; i=i+1 ))
do
if [ "$i" -eq 4 ]
then
break
fi
echo $i
done
----
#猜数字游戏
[root@localhost ~]# cat guess.sh
#!/bin/bash
read -sp "please enter the answer:" num
echo
while true
do
read -p "please guess a num:" num1
if [ "$num1" -gt "$num" ]
then
echo "大了"
elif [ $num1 -eq $num ]
then
echo "对了"
break
else
echo "小了"
fi
done
continue
- continue概述:忽略本次循环剩余的代码,直接进行下一次循环;在for、while等循环语句中,用 于跳出当次所在的循环,不终止循环,继续执行剩余循环
#还用刚才的脚本,把break换成continue
#!/bin/bash
for ((i=1; i<=10; i=i+1 ))
do
if [ "$i" -eq 4 ]
then
continue
fi
echo $i
done
函数
- 在编写脚本时,有些语句会被重复使用多次。把这些可能重复使用的代码写成函数,这样我们通过函数名称可以更高效的重复利用他们。如果想让自己写的脚本代码可以为别人所使用,同样需要函数功能。
格式:
function 函数名() {程序
}
#!/bin/bash
function demoFun(){
echo "这是我的第一个 shell 函数!"
}
echo "-----函数开始执行-----"
demoFun
echo "-----函数执行完毕-----"
----------------------
#定义一个求和函数,计算从1到用户输入数字的和
$ vim function.sh
#!/bin/bash
#定义函数
function sum () {
s=0
for (( i=0;i<=$1;i=i+1 ))
do
s=$(( $i+$s ))
done
echo "the sum of 1+2+3+..+$1 is :$s"
}
-----------------
#本脚本直接调用该函数,继续写
$ vim function.sh
read -p "please input a number:" num
y=$(echo $num | sed 's/[0-9]//g')
if [ -z "$y" ]
then
sum $num
else
echo "error!!please input a number!"
fi
-------------------
#其他脚本调用
$ cat diaoyong.sh
#!/bin/bash
#先用.或sourceh引入函数所在的脚本
. /root/function.sh
#再用函数名调用
read -p "please input a number:" num
y=$(echo $num | sed 's/[0-9]//g')
if [ -z "$y" ]
then
sum $num
else
echo "error!!please input a number!"
fi