shell 编程运维实战 三剑客

一 grep

一 目的:
过滤,查找文档中的内容

二 分类:
1.grep
2.egrep
扩展支持正则
3.fgrep
不支持正则

三 返回值:
0是找到了,代表成功
1是没有,表示在所提供的文件无法找到匹配的pattern
2代表地儿不对
示例:

grep ‘root’ /etc/passwd

root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

echo $?

0

grep ‘root1’ /etc/passwd #用户root1并不存在

echo $?

1

grep ‘root’ /etc/passwd1 #这里的/etc/passwd1文件并不存在

grep: /etc/passwd1: No such file or directory

echo $?

2

四 参数;
grep -q 静默
[root@localhost ~]# grep “5…” 1.txt
5.1
5.2
5.a
5.b
5…
5…
5.aaa
[root@localhost ~]# grep -q “5…” 1.txt
[root@localhost ~]# echo $?
0

grep -v 去反
grep -R 可以查目录下面的文件
[root@localhost ~]# grep xulei /home/
grep: /home/: 是一个目录
[root@localhost ~]# grep -R xulei /home/
/home/xulei/.cache/gdm/session.log
/home/xulei/.cache/imsettings/log [HOME=/home/xulei/.config/imsettings]
匹配到二进制文件 /home/xulei/.cache/tracker/meta.db
匹配到二进制文件 /home/xulei/.cache/tracker/meta.db-wal

grep -o 只找到这个关键字就可以
[root@localhost ~]# grep “a” 1.txt
5.a
5.aaa
a
ab
abc
abcd
abcde
abcdef
loveable
ldfadasfsdave

[root@localhost ~]# grep -o  "a" 1.txt

a
a
a
a
a
a
a
a
a
a
a
a
a
a

grep -B2前两行
grep -A2后两行
grep -C2上下两行
egrep -l 只要文件名
[root@localhost ~]# egrep -l ‘root’ /etc/passwd
/etc/passwd
egrep -n 带行号
[root@localhost ~]# egrep -n ‘xulei’ /etc/passwd
43:xulei❌1000:1000::/home/xulei:/bin/bash

五 示例:
grep -E 或 egrep 进行使用
#egrep ‘NW’ datafile.txt
在datafile文件中,找NW
#egrep ‘NW’ d*.txt
找NW,文件只要是d开头就可以
#egrep ‘^n’ datafile.txt
以n开头的
#egrep ‘4$’ datafile.txt
4结尾
#egrep TB Savage datafile.txt
找TB,在savage里找,在datafile里找
#egrep ‘TB Savage’ datafile.txt
找TB Savage
#egrep ‘5…’ datafile.txt
找五点后面是一个任意字符
#egrep ‘.5’ datafile
找点五
#egrep ‘1’ datafile.txt
W或E开头
#egrep ‘[^0-9]’ datafile
不是0到9
#egrep ‘[A-Z][A-Z] [A-Z]’ datafile
俩大写,空格,大写的行
#egrep 'ss* ’ datafile
s开头,0到多个s
#egrep ‘[a-z]{9}’ datafile
小写字母出现9次
#egrep ‘ 单词开头
#egrep ‘’ datafile
就找这个词
#egrep ‘<[a-r].n>’ datafile
a或r开头,中间任意,n结尾
#egrep '^n\w
\W’ datafile
n开头,多个字母,结尾一个非字母
#egrep ‘\bnorth\b’ datafile
就找north
#egrep ‘NW|EA’ datafile
NW或者EA
#egrep ‘3+’ datafile
1个或多个3
#egrep ‘2.?[0-9]’ datafile
2开头,0个或1个点,一个数字
#egrep ‘(no)+’ datafile
一个或多个no
#egrep ‘S(h|u)’ datafile
sh或su
#egrep ‘Sh|u’ datafile
sh或者u

二 sed

一 前言:
sed 是一种在线的、非交互式的编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。

接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;

二 格式:
1 sed 选项 命令 文件
sed [options] ‘command’ file(s)
2 sed 选项 –f 脚本 文件
sed [options] -f scriptfile file(s)

三 返回值:
都是0,对错不管。
只有当命令存在语法错误时,sed的退出状态才是非0

四 sed和正则表达式:
与grep一样,sed在文件中查找模式时也可以使用正则表达式(RE)和各种元字符。正则表达式是括在斜杠间的模式,用于查找和替换,以下是sed支持的元字符。
使用基本元字符集 ^, $, ., *, [], [^], < >,(),{}
使用扩展元字符集 ?, +, |, ( )
使用扩展元字符的方式:
+ 转义
sed -r 加-r

五 汇总示例;
1.删除命令: d
2.替换命令: s
3.读文件命令:r
4.写文件命令:w(另存为)
5.追加命令: a(之后)
6.插入命令: i(之前)
7.替换整行命令: c
8.获取下一行命令:n
9.反向选择: !
10.多重编辑 e

六 实战案例:
1.删除配置文件中#号注释行
# sed -r ‘/^#/d’ /etc/vsftpd/vsftpd.conf

2.修改文件:
# sed -ri ‘$a\chroot_local_user=YES’ /etc/vsftpd/vsftpd.conf
在最后一行追加
# sed -ri ‘/^SELINUX=/cSELINUX=disabled’ /etc/selinux/config
换行c

3.给文件行添加注释:
# sed -r ‘2,6s/^/#/’ a.txt
找到2到6行,把开始换成#
# sed -r ‘2,6s/(.)/#\1/’ a.txt
()内容可以被\1引用
# sed -r '2,6s/.
/#&/’ a.txt
&匹配前面查找的内容

4.sed中使用外部变量
准备工作
# var1=11111
追加变量
# sed -r "1a v a r 1 " / e t c / h o s t s 第 一 行 后 追 加 变 量 var1" /etc/hosts 第一行后追加变量 var1"/etc/hostsvar1
[root@localhost ~]# sed -r "1a v a r 1 " / e t c / h o s t s 127.0.0.1 l o c a l h o s t l o c a l h o s t . l o c a l d o m a i n l o c a l h o s t 4 l o c a l h o s t 4. l o c a l d o m a i n 4111111 : : 1 l o c a l h o s t l o c a l h o s t . l o c a l d o m a i n l o c a l h o s t 6 l o c a l h o s t 6. l o c a l d o m a i n 6 注 意 调 用 变 量 时 , 使 用 单 引 号 是 错 误 的 。 巧 合 s e d − r i " var1" /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 111111 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 注意调用变量时,使用单引号是错误的。 巧合 sed -ri " var1"/etc/hosts127.0.0.1localhostlocalhost.localdomainlocalhost4localhost4.localdomain4111111::1localhostlocalhost.localdomainlocalhost6localhost6.localdomain6使sedri"a v a r 1 " / e t c / h o s t s 报 错 了 , var1" /etc/hosts 报错了, var1"/etc/hostsa最后一行追加被误认为变量了。
# sed -r “$a$var1” /etc/hosts

三 awk

一 前言:
awk 是一种编程语言,用于在linux/unix下对文本和数据进行处理。
数据可以来自标准输入、一个或多个文件,或其它命令的输出。
它支持用户自定义函数和动态正则表达式等先进功能,

awk的处理文本和数据的方式是这样的,
它逐行扫描文件,从第一行到最后一行,
寻找匹配的特定模式的行,并在这些行上进行你想要的操作。
如果没有指定处理动作,则把匹配的行显示到标准输出(屏幕),

awk分别代表其作者姓氏的第一个字母。因为它的作者是三个人,
分别是Alfred Aho、Peter Weinberger、 Kernighan。

二 工作原理:
#awk -F: ‘{print $1,$3}’ /etc/passwd
(1)awk使用一行作为输入,并将这一行赋给内部变量$0,每一行也可称为一个记录,以换行符结束

(2)然后,行被:(默认为空格或制表符)分解成字段(或域),每个字段存储在已编号的变量中,从$1开始,
最多达100个字段

(3)awk输出之后,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔
成字段并进行处理。该过程将持续到所有行处理完毕

三 语法:
awk [options] ‘commands’ filenames (推荐)
==options:
例如:-F 定义输入字段分隔符,默认的分隔符是空格或制表符(tab)
==command(时空):
BEGIN{} {} END{}
BEGIN{}
begin发生在行处理前(注意大写)
{}
行处理时,读一行执行一次
END{}
行处理后
示例
#awk ‘BEGIN{print 1/2} {print “ok”} END{print “-----------”}’ /etc/hosts

四 内部变量:
1.FS
输入字段分隔符(默认空格)
[root@localhost ~]# awk -F: ‘{print $1, $3}’ /etc/passwd | head -1
root 0

[root@localhost ~]# awk -F’[ :\t]’ ‘{print $1,$2,$3}’ /etc/passwd | head -1
root x 0

[root@localhost ~]# awk ‘BEGIN{FS=":"} {print $1,$3}’ /etc/passwd | head -1
root 0

2.OFS
输出字段分隔符
[root@localhost ~]# awk -F: ‘{print $1,$2,$3,$4}’ /etc/passwd | head -1
root x 0 0

[root@localhost ~]# awk -F: ‘BEGIN{FS=":";OFS="+++"}{print $1,$2,$3,$4}’ /etc/passwd | head -1
root+++x+++0+++0

3.RS
输入记录(行)分隔符,默认换行符
[root@localhost ~]# awk ‘{print $0}’ a.txt
111 222 333 444 555:666:777
[root@localhost ~]# awk ‘BEGIN{RS=" "}{print $0}’ a.txt
111
222
333
444
555:666:777
请注意,在此时记录已经不是行的概念了。分隔符由”换行符“换成了”空格“

4.ORS
输出记录(行)分隔符,默认换行符

5.FNR
多文件独立编号
同下

6.NR
多文件汇总编号
[root@localhost ~]# awk -F: ‘{print NR, $0}’ /etc/centos-release /etc/hosts
1 CentOS Linux release 7.3.1611 (Core)
2 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
3 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
[root@localhost ~]# awk -F: ‘{print FNR, $0}’ /etc/centos-release /etc/hosts
1 CentOS Linux release 7.3.1611 (Core)
1 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
2 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6

7.NF
字段总数
[root@localhost ~]# awk -F: ‘{print NF, $0}’ /etc/passwd
7 root❌0:0:root:/root:/bin/bash
7 bin❌1:1:bin:/bin:/sbin/nologin
7 daemon❌2:2:daemon:/sbin:/sbin/nologin

[root@localhost ~]# awk -F: ‘{print NF, $NF}’ /etc/passwd
7 /bin/bash
7 /sbin/nologin
7 /sbin/nologin

五 格式化输出:
print 函数

[root@localhost ~]# date |awk ‘{print "Month: " $2 "\nYear: " $1}’
Month: 11月
Year: 2017年
\n换行符
想输出文字,用引号
[root@localhost ~]# awk -F: ‘{print "username is: " $1 "\t uid is: " $3}’ /etc/passwd | head -1
username is: root uid is: 0
[root@localhost ~]# awk -F: ‘{print "\tusername and uid: " $1,$3 “!”}’ /etc/passwd | head -1
username and uid: root 0!

六 模式(正则表达式)和动作:
概念
任何awk语句都由模式和动作组成。模式部分决定动作语句何时触发及触发事件。
如果省略模式部分,动作将时刻保持执行状态。每一行都会有动作。
模式可以是任何条件语句或复合语句或正则表达式。有模式的话,就是对模式对应的行进行动作。
模式:可以是条件测试,正则,复合语句
动作:可以是打印,计算等。
字符串比较
# awk ‘/^root/’ /etc/passwd
#awk ‘$0 ~ /^root/’ /etc/passwd
#awk ‘$0!~/^root/’ /etc/passwd
#awk -F: ‘$1 ~ /^root/’ /etc/passwd
数值比较
目的
比较表达式采用对文本进行比较,只有当条件为真,才执行指定的动作。比较表达式使用关系运算符,
用于比较数字与字符串。
关系运算符
语法
运算符 含义 示例
< 小于 x <= 小于或等于 x<=y
== 等于 x==y
!= 不等于 x!=y

= 大于等于 x>=y
大于 x>y
示例
# awk -F: ‘$3 == 0’ /etc/passwd
#awk -F: ‘$3 == 1’ /etc/passwd
#awk -F: ‘$3 < 10’ /etc/passwd
== 也可以用于字符串判断
#awk -F: ‘$7 == “/bin/bash”’ /etc/passwd
#awk -F: ‘$1 == “alice”’ /etc/passwd
算术 运算
语法
+ - * / %(模) (幂23)
示例
# awk -F: ‘$3 * 10 > 500’ /etc/passwd
多条件
逻辑操作符和复合模式
语法
&& 逻辑与 a&&b
|| 逻辑或 a||b
! 逻辑非 !a
示例
# awk -F: ‘$1~/root/ && $3<=15’ /etc/passwd
#awk -F: ‘$1~/root/ || $3<=15’ /etc/passwd
#awk -F: ‘!($1~/root/ || $3<=15)’ /etc/passwd
范围模式
语法
awk ‘/从哪里/,/到哪里/’ filename
示例
# awk -F: ‘/adm/,/lpd/’ /etc/passwd
从adm到ldp,显示出来,注意避免匹配重复的字段。

七 awk脚本编程:
变量
awk调用变量
自定义内部变量 -v
awk -v user=root -F: ‘$1 == user’ /etc/passwd
-v定义变量
外部变量 “ ‘ ”
单引号
[root@localhost ~]# heihei=shutdown
[root@localhost ~]# echo $heihei
shutdown
[root@localhost ~]# awk -F: ‘ 1   " ′ " 1 ~ "'" 1 ""heihei"’" ’ passwd
shutdown❌6:0:shutdown:/sbin:/sbin/shutdown7
注意使用单引号时,内部需要用双引转义
条件&判断
if语句
格式
{if(表达式){语句;语句;…}}
示例
需求
如果$3是0,就说他是管理员
awk -F: ‘{if($30) {print $1 " is administrator."}}’ /etc/passwd
if…else语句
格式
{if(表达式){语句;语句;…}else{语句;语句;…}}
{if(){}else{}}
示例
需求
统计管理员和系统用户数量
示例
awk -F: '{if($3
0){count++} else{i++}} END{print "管理员个数: "count ; print "系统用户数: "i}’ /etc/passwd
if…else if…else语句:
格式
{if(表达式1){语句;语句;…}else if(表达式2){语句;语句;…}else if(表达式3){语句;语句;…}else{语句;语句;…}}
if (条件){动作}elseif(条件){动作}else{动作}
if(){}else if (){}else if(){}else{}
示例
需求
题目:显示出三种用户的信息
管理员:管理员ID为0

内置用户:用户ID<1000

普通用户: 用户ID>999
示例
[root@localhost ~]# awk -F: ‘{if($3==0){print $1," is admin “}else if ($3>999){print $1,” is user"}else {print $1, " is sofo user"}}’ /etc/passwd
示范
root is admin
bin is sofo user
daemon is sofo user
adm is sofo user
lp is sofo user
sync is sofo user
shutdown is sofo user
halt is sofo user
mail is sofo user
operator is sofo user
games is sofo user
ftp is sofo user
nobody is sofo user
systemd-network is sofo user
dbus is sofo user
polkitd is sofo user
tss is sofo user
postfix is sofo user
chrony is sofo user
sshd is sofo user
aofa is user
apache is sofo user

	示例
		题目:统计出三种用户的数量

管理员数量:管理员ID为0

内置用户数量:用户ID<1000

普通用户数量: 用户ID>999
awk -F: '{

if($3==0){i++}
else if($3>999){k++}
else{j++}

}

END{
print i;
print k;
print j

}’ /etc/passwd
示例
awk -F: ‘{if($30){i++} else if($3>999){k++} else{j++}} END{print i; print k; print j}’ /etc/passwd
升级
awk -F: '{if($3
0){i++} else if($3>999){k++} else{j++}} END{print "管理员个数: "i; print "普通用个数: "k; print "系统用户: "j}’ /etc/passwd
循环
while
每行打印十次
awk ‘{i=1;whilw(i<=10){print $0;i++}}’ passwd
for
循环打印5个数字
awk ‘BEGIN{for(i=1;i<=5;i++){print i} }’
不适用BEGIN可以吗?
{},和END{}可以吗?可以需要有文件。
将每行打印10次
awk -F: ‘{ for(i=1;i<=10;i++) {print $0} }’ /etc/passwd
打印每一行的每一列
awk -F: ‘{ for(i=1;i<=NF;i++) {print $i} }’ passwd
说明:NF是最大列数,循环打印了每一列。
# awk -F: ‘{ for(i=1;i<=NF;i++) {print $i} }’ passwd
root
x
0
0
root
/root
/bin/bash
数组
定义数组
需求
将用户名定义为数组的值,打印第一个值
# awk -F: ‘{username[++i]=$1} END{print username[1]}’ /etc/passwd
root
数组遍历
按索引遍历
[root@localhost ~]# awk -F: ‘{username[x++]=$1} END{for(i in username) {print i,username[i]} }’ /etc/passwd
10 games
11 ftp
12 nobody
13 systemd-bus-proxy
14 systemd-network
15 dbus
16 polkitd
30 chrony
17 abrt
31 ntp
18 unbound

awk编程案例
统计/etc/passwd中各种类型shell的数量
awk -F: ‘{shells[$NF]++} END{ for(i in shells){print i,shells[i]} }’ /etc/passwd
提示
$NF 最后一列的字段内容
{}行处理
把统计的对象,作为索引。每次递增。
Print i 打印索引
Shells[i] 数组加索引,显示的就是值。


  1. we ↩︎

你可能感兴趣的:(shell)