shell脚本-grep、sed、awk三剑客


介绍

Shell 脚本中的 grep、sed 和 awk 被称为 “三剑客”,它们是文本处理中的利器。grep 用于快速搜索文本,sed 则是对文本进行编辑和转换,而 awk 则提供了更强大的文本处理能力,可以按字段分割、执行条件操作等。这三者结合起来,能够处理各种文本处理任务,如日志分析、数据提取、格式转换等。

基本正则表达式

正则表达式的基本组成部分

  • ^:行起始标记
  • $:行尾标记
  • .:匹配任意一个(单个)字符
  • []:匹配包含在[字符]之中的任意一个字符
  • [^]:匹配除[^字符]之外的任意一个字符
  • [-]:匹配中指定范围内的任意一个字符
  • ?:匹配之前的项1或0次
  • +:匹配之前的项1次或多次
  • *:匹配之前的项0或多次
  • ():创建一个用于匹配的字串
  • {n}:匹配之前的项n次
  • {n,}:之前的项至少需要匹配n次
  • {n,m}:指定之前的项所必需匹配
  • |:交替匹配|两边的任意一项
  • \:转义字符可以将上面的特殊字符进行转义

案例

  • ^linux: 以"linux"开头
  • linux$: 以"linux"结尾
  • Alex.: 匹配"Alexs"等
  • coo[kl]: 匹配"cool"或"cook"
  • 9[^5689]: 匹配"91"、“92"等,但不匹配"95”、"98"等
  • [0-9]: 匹配任意一个数字
  • [a-z]|[A-Z]: 匹配任意一个大小写字母
  • colou?r: 匹配"color"或"clolur",但不能匹配"colouur"
  • rollno-9+: 匹配"rollno-9"、“rollno-99”、“rollno-999”,但不匹配"rollno-"
  • co*l: 匹配"cl"、“col”、“cool”、"coool"等
  • ma(tri)x: 匹配"matrix"
  • [0-9]{3}: 匹配任意一个三位数,等同于[0-9][0-9][0-9]
  • [0-9]{2,}: 匹配任意一个两位数或更多位的数字
  • [0-9]{2,5}: 匹配从两位数到五位数之间的任意一个数字
  • Oct (1st|2nd): 匹配"Oct 1st"或"Oct 2nd"
  • a\.b: 匹配"a.b",但不能匹配"ajb"
  • [a-z0-9_]+\@[a-z0-9_]+\.[a-z]{2,4}: 匹配一个邮箱地址
  • [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}: 匹配IP地址

grep用法

grep 的作用是检索,即根据条件查找目标文件中的匹配内容。

语法格式:
grep [选项] [条件表达式] 目标文件

常用选项:

  • -e:匹配多个表达式
  • -R:递归搜索
  • -n:显示匹配的行号
  • -o:只输出匹配内容
  • -E:支持扩展正则表达式
  • -c:统计匹配次数
  • -v:取反,即显示不匹配的内容
  • -l:显示匹配的文件名
  • -L:显示不匹配的文件名
  • -A:显示匹配行后的行数
  • -B:显示匹配行前的行数

案例

[root@ns ~]# cat /proc/meminfo | grep -e Mem -e Cache -e Swap  # 查看系统内存、缓存、交换分区
[root@ns ~]# grep -R -o -n -E '[a-z0-9_]+\@[a-z0-9_]+\.[a-z]{2,4}' /etc/  # 查找/etc目录下的所有文件中的邮件地址;-R递归,-n表示匹配的行号,-o只输出匹配内容,-E支持扩展正则表达式
[root@ns ~]# grep -R -c 'HOSTNAME' /etc/ | grep -v "0$"  # 查找/etc/目录下文件中包含“HOSTNAME”的次数,-c统计匹配次数,-v取反
[root@ns ~]# grep -R -l 'HOSTNAME' /etc/  # 查找包含“HOSTNAME”的文件名,-l显示匹配的文件名,-L显示不匹配的文件名
[root@ns ~]# dmesg | grep -n --color=auto 'eth'  # 查找内核日志中eth的行,显示颜色及行号
[root@ns ~]# dmesg | grep -n -A3 -B2 --color=auto 'eth'  # 用 dmesg 列出核心信息,再以 grep 找出内含 eth 那行,在关键字所在行的前两行与后三行也一起找出出来显示
[root@ns ~]# cat /etc/passwd | grep -c bash$  # 统计系统中能登录的用户的个数
[root@ns tmp]# touch /tmp/{123,123123,456,1234567}.txt  # 创建测试文件,以下三条命令是一样的效果,匹配文件名123,可以包含1个到多个
[root@ns tmp]# ls | grep -E '(123)+' 
[root@ns tmp]# ls | grep '\(123\)\+'
[root@ns tmp]# ls | egrep '(123)+'
[root@ns ~]# ps -ef | grep -c httpd  # 统计httpd进程数量
[root@ns ~]# grep -C 4 'games' --color /etc/passwd  # 显示games匹配的“-C”前后4行
[root@ns ~]# grep ^adm /etc/group  # 查看adm组的信息
[root@ns ~]# ip a | grep -E '^[0-9]' | awk -F : '{print $2}'  # 获取网卡名称
[root@ns ~]# ifconfig eth0 | grep -E -o 'inet addr:[^ ]*' | grep -o '[0-9.]*'  # 截取ip地址,[^ ]*表示以非空字符作为结束符,[0-9.]*表示数字和点的组合
[root@ns ~]# ifconfig eth0 | grep -i hwaddr | awk '{print $5}'  # 截取MAC地址
[root@ns ~]# cat test.txt  # 测试文档:使用grep -E命令练习正则表达式
[root@ns tmp]# cat test.txt 
ABcd
10.10.10.10
color
colur
Alex2
colouur
rollno-9
i like linux
linux funny.
I am Alex.
rollno-99
rollno-999
cl
col
cool
coool
cook
max
matrix
192.168.100.100
123456789
123123123
123123
23346123
123
12
12345
94
95
96
97
98
99
100
Oct 1st
Oct 2nd
a.b
ajb
abc
Alex@qq.com
linux
[root@ns tmp]# 

sed流编辑器

sed流编辑器:实现对文本的增删改查

# 改
[root@ns ~]# sed 's/dhcp/static/g' /etc/sysconfig/network-scripts/ifcfg-eth1  # 只是显示,不修改
[root@ns ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth1
[root@ns ~]# sed -i 's/dhcp/static/g' /etc/sysconfig/network-scripts/ifcfg-eth1  # 只修改,不显示
[root@ns ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth1  # 验证
[root@ns ~]# vim ip  # 编写测试文件 
IP1=static
IP2=static
IP=static
:wq
[root@ns ~]# sed -i 's/dhcp/static/g' ip  # 将所有的dhcp替换为static
[root@ns ~]# sed -i '/^IP1/s/static/dhcp/g' ip  # 将IP1开头的行替换
[root@ns ~]# sed -i '2s/static/dhcp/g' ip  # 指定特定行号2行替换
[root@ns ~]# cat -n /etc/selinux/config  # 查看并显示行号
[root@ns ~]# sed -i '7s/disabled/enforcing/g' /etc/selinux/config  # 开启selinux
[root@ns ~]# sed -i '7s/disabled/enforcing/g' /etc/selinux/config

# 删
[root@ns ~]# vim  ip  # 添加空行
[root@ns ~]# sed '/^$/d' ip  # 删除空行并显示在屏幕上
[root@ns ~]# sed -i '/IP1/d' ip  # 删除包含IP1的行
[root@ns ~]# sed -i '/^IP2/d' ip  # 删除以IP2开头的行
[root@ns ~]# sed -i '2d' ip  # 删除第二行

# 增
[root@ns ~]# sed 'a IP3=static' ip  # 每一行后都加上IP3=static
[root@ns ~]# sed '3a IP3=static' ip  # 只在第3行后加上IP3=static,并显示不修改
[root@ns ~]# sed '3i IP3=static' ip  # 只在第3行前加上IP3=static,显示不修改
[root@ns ~]# sed -i '3a IP3=static' ip  # 修改,不显示
[root@ns ~]# sed -i '/^IP3/a "test add"' ip  # 在以IP3开头的行后添加

# 查
[root@ns ~]# sed -n '2p' /etc/hosts  # 查看第二行
[root@ns ~]# sed -n '/www/p' /var/named/chroot/var/named/Alex.cn.zone  # 查看包含www的解析记录
[root@ns ~]# sed -n '/.100$/p' /var/named/chroot/var/named/Alex.cn.zone  # 查看以.100结尾的行
[root@ns ~]# sed -n '2~2p' ip  # 从第二行,每隔两行显示

awk:报告生成器

# awk的作用是将文件格式化后显示信息。
# 语法:
awk [选项] '模式{ 动作(action) }' 文件1 文件2 ...

# 最常见的动作:print, printf

# awk的基本原理特征:
# 1)每一次取一行
# 2)根据指定的分隔符(不指定是位空白字符)将该行切割位列,使用$0(整行),$1,$2,$3...(第一列,第二列,...)
# 3)可以指定行号,列号,切割符,操作后分隔符

在这里插入图片描述

案例

chkconfig --list | grep 3:启用 | awk '{print $1}'
tail -1 /etc/passwd | awk -F ':' 'BEGIN{OFS="---"}{print $1,$6,$7}'   # OFS指定输出分隔符
ifconfig eth1 | awk -F '[ :]+' 'NR==2 {print $4}'
ifconfig eth1 | awk -F '[ :]+' 'NR==2 {print "eth1_ip="$4}'  # 可以加入显示内容
awk 'BEGIN {print "line one \nline two\nline three"}'

awk区块原理

每一条编辑指令若包含多条语句,则以分号隔开。如果有多条编辑指令,则使用以分号或者空格分隔的多个{ }区域。

区域构成

区块
BEGIN { 动作 } ## 开始处理第一行文本之前的操作
{ 动作 } ## 针对每一行文本的处理操作
END { 动作 } ## 处理完最后一行文本之后的操作

awk 的执行流程

  • 1)首先执行 BEGIN { } 区块中的初始化操作;
  • 2)然后从指定的数据文件中循环读取一个数据行(自动更新 NF、 NR、 $0、 $1…… 等内建变量的值),并执行’模式或条件{ 动作 }';
  • 3)最后执行 END { } 区块中的后续处理操作。

awk高级使用

1. AWK 变量

  • FS:列分隔符,默认为空白
  • RS:行分隔符,默认为换行符
  • OFS:输出列分隔符
  • ORS:输出行分隔符

2. AWK 内置变量

  • NR:处理中的行数
  • FNR:单个文件的行数
  • NF:列的个数

在 MarkdownPad2 中,你可以将 awk 操作符优化为如下形式:

awk操作符

1. 算数操作符

  • -x
  • +x
  • x^y
  • x**y
  • x*y
  • x/y
  • x+y
  • x-y
  • x%y

2. 赋值操作符

  • =
  • +=
  • -=
  • *=
  • /=
  • %=
  • ++
  • --

3. 布尔值

在 awk 中,任何非零值或非空字符串都为真,反之为假。

4. 比较操作符

  • >
  • <
  • >=
  • <=
  • ==
  • !=
  • ~ (字符串能被表达式 y 匹配)
  • !~ (字符串不能被表达式 y 匹配)

5. 逻辑操作符

  • &&
  • ||
  • !

awk常见的模式类型

1. 正则表达式 (regexp)

使用 /regular expression/ 格式的正则表达式进行匹配。

awk -F : '/^u/{print $1}' /etc/passwd

2. 表达式 (expression)

使用值非零或非空字符串的表达式进行条件判断。

awk -F : '$3>=500{print $1,$3,$7}' /etc/passwd  # 打印普通用户
awk -F : '$3+1<=100 && $3+1>=10{print $1,$3,$7}' /etc/passwd  # UID在10-100之间的用户
awk -F : '$2=="!!"{print $1,$2}' /etc/shadow  # 检查未初始化密码的用户
passwd -d u01
awk -F : '$2==""{print $1}' /etc/shadow  # 打印密码为空的用户
awk -F : '$7~"bash$"{print $1,$3,$7}' /etc/passwd  # 匹配$7为bash结束行
awk -F : '$7!~"bash$"{print $1,$3,$7}' /etc/passwd

3. 匹配范围 (ranges)

指定的匹配范围,格式为 part1,part2

awk -F : '$3==3,$3==10{print $1,$3,$7}' /etc/passwd
awk -F : '$1=="root",$1=="adm"{print $1,$3,$7}' /etc/passwd
awk -F : '/^r/,/^a/{print $1,$3,$7}' /etc/passwd

4. 特殊模式 (BEGIN/END)

在处理开始前(BEGIN)或处理结束后(END)执行的特殊模式。

awk -F : 'BEGIN{printf "%-10s%-10s%-20s\n","UserName","ID","Shell"}{printf "%-10s%-10s%-20s\n",$1,$3,$7}' /etc/passwd  # 在处理前打印头部
awk -F : 'BEGIN{printf "%-10s%-10s%-20s\n","UserName","ID","Shell"}$7~"bash$"{printf "%-10s%-10s%-20s\n",$1,$3,$7}' /etc/passwd  # 多个模式混合使用
awk -F : 'BEGIN{printf "%-10s%-10s%-20s\n","UserName","ID","Shell"}$7~"bash$"{printf "%-10s%-10s%-20s\n",$1,$3,$7}END{print "End of report"}' /etc/passwd  # 在处理后打印尾部
awk -v i=1 '$5~"yum"{i++}END{print "yum use times:", i}' /var/log/messages

awk 中数组的使用

数组的使用

在 awk 中,数组的下标(index-expression)可以是任意字符串。如果某个数组元素事先不存在,awk 会自动创建该元素并初始化为空串。要判断数组中是否存在某个元素,需要使用 下标 in 数组 的方式。要遍历数组中的每个元素,可以使用特殊结构 for (变量 in 数组) { 语句1, ... }

例如:

state[abc]=3
state[efg]=6
for (A in state) {
    print A,state[A]
}

其中,Aindex-expression(下标abc/efg),state[A] 是具体的值(3/6)。

示例

假设我们有以下 netstat -ant 命令的输出:

Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address    Foreign Address        State      
tcp        0      0 0.0.0.0:22       0.0.0.0:*           LISTEN      
tcp        0      0 127.0.0.1:25     0.0.0.0:*           LISTEN      
tcp        0      0 192.168.1.106:22 192.168.1.101:36318 ESTABLISHED 

若要统计每种状态的个数,只需将 state 作为数组下标,然后使用 ++ 自增操作即可。

netstat -ant | awk '/^tcp|^udp/ {state[$6]++} END {for (i in state) {print i,state[i]}}'

这段代码中,state[$6]++ 表示定义数组 state[]$6 是下标,++ 表示自增1。

另一个示例是使用 awk 遍历 /etc/passwd 文件中的每一行,如果用户的 shell 是 bash,且其 UID 在 1 到 500 之间,则将其 shell 修改为 /sbin/nologin

for i in $(awk -F : '$7~"bash$" {if($3 > 1 && $3 < 500) print $1}' /etc/passwd); do
    chsh -s /sbin/nologin $i;
done
  • 21
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XMYX-0

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

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值