awk/sed/grep从入门到放弃(练习)
三剑客配合正则,从学习到使用,到熟练使用,再到得心应手,需要经过大量的练习沉淀,所以笔者准备了一些小练习,供大家进阶。
1. 概述
1.1 三剑客功能对比
三剑客 | 通用功能 | 独有功能 |
---|---|---|
grep/egrep | 过滤 | 过滤快,取反(也算删除) |
sed | 过滤 | 替换,修改文件内容,增加,删除,取行 |
awk | 过滤 | 取列(取行),精确过滤(某一列包含),统计计算 |
1.2 正则
基础正则,扩展正则,通配符,linux中的特殊符号,笔者总是搞混,这里按使用类型简单做了整理
- 表示重复(连续出现)
符号 | 说明 | 所属 |
---|---|---|
+ | 1次及以上,一般和[] 一起使用 | 扩展 |
* | 0次及以上,一般和. 一起使用 | 基础 |
? | 0次或1次 | 扩展 |
{n,m} | {n,m}连续出现多次,大于n,小于m次 | 基础 |
- 表示或者
符号 | 说明 | 所属 |
---|---|---|
[a,b] | 等于a或者b | 基础 |
[a-b] | 等于a到b之间的某个字符 | 基础 |
[^a-b] | 除了a到b之间的某个字符 | 基础 |
- 其他字符
符号 | 说明 | 所属 |
---|---|---|
. | 任意字符,默认一定有一个字符 | 基础 |
.* | 任意长度字符,但不一定有字符 | 基础 |
^ | 一般用来匹配行首,如果在[^] 则表示排除中括号中的内容 | 基础 |
$ | 一般用来匹配行尾 | 基础 |
^$ | 一般用来匹配空行 | 基础 |
() | 在sed中用来反向引用,在扩展正则中表示群组 | 扩展 |
详细参考 https://blog.csdn.net/u010230019/article/details/138622110
2. sed 回顾
这里引入sed的各种空间各自模式,避免走火入魔,有兴趣可以看看
SECTION 25 sed进阶(一)
SECTION 25 sed进阶(二)
我们准备一个练习文件,用于使用
[root@node-252 exercises]# cp /etc/passwd .
[root@node-252 exercises]# head -3 passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
2.1 查找(过滤)
一般格式:
sed -n '[定位]p' filename
-n
:取消默认输出
p
:print
打印匹配内容
- 查找第2行
[root@node-252 exercises]# sed -n '2p' passwd
bin:x:1:1:bin:/bin:/sbin/nologin
- 查找第2到5行
[root@node-252 exercises]# sed -n '2,5p' passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
包括第5行哦
[root@node-252 exercises]# cat -n passwd
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
- 查找匹配
ntp
的行
[root@node-252 exercises]# sed -n '/ntp/p' passwd
ntp:x:38:38::/etc/ntp:/sbin/nologin
- 查找匹配
ftp
到ntp
的行
[root@node-252 exercises]# sed -n '/ftp/,/ntp/p' passwd
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
- 查找包含
ftp
和ntp
的行
[root@node-252 exercises]# sed -rn '/ftp|ntp/p' passwd
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
这里需要使用-r
选项,能够让sed
支持扩展正则,|
是扩展正则的符号
- 查找包含大写字母的行
[root@node-252 exercises]# sed -rn '/[[:upper:]]/p' passwd
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
nginx:x:997:995:Nginx web server:/var/lib/nginx:/sbin/nologin
rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
gluster:x:996:992:GlusterFS daemons:/run/gluster:/sbin/nologin
unbound:x:994:990:Unbound DNS resolver:/etc/unbound:/sbin/nologin
saslauth:x:993:76:Saslauthd user:/run/saslauthd:/sbin/nologin
rtkit:x:172:172:RealtimeKit:/proc:/sbin/nologin
tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
saned:x:992:988:SANE scanner daemon user:/usr/share/sane:/sbin/nologin
pulse:x:171:171:PulseAudio System Daemon:/var/run/pulse:/sbin/nologin
colord:x:990:984:User for colord:/var/lib/colord:/sbin/nologin
geoclue:x:989:983:User for geoclue:/var/lib/geoclue:/sbin/nologin
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
sssd:x:988:982:User for sssd:/:/sbin/nologin
avahi:x:70:70:Avahi mDNS/DNS-SD Stack:/var/run/avahi-daemon:/sbin/nologin
- 查找包含
daemon
的行,不区分大小写
[root@node-252 exercises]# sed -n '/daemon/Ip' passwd
daemon:x:2:2:daemon:/sbin:/sbin/nologin
rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
gluster:x:996:992:GlusterFS daemons:/run/gluster:/sbin/nologin
libstoragemgmt:x:995:991:daemon account for libstoragemgmt:/var/run/lsm:/sbin/nologin
tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
saned:x:992:988:SANE scanner daemon user:/usr/share/sane:/sbin/nologin
pulse:x:171:171:PulseAudio System Daemon:/var/run/pulse:/sbin/nologin
avahi:x:70:70:Avahi mDNS/DNS-SD Stack:/var/run/avahi-daemon:/sbin/nologin
I
能够在匹配的时候忽略大小写
2.2 替换
一般格式:
sed -n 's#匹配内容#替换内容#gp' filename
#s/regexp/replacement/
我们使用#@$
等特殊符号替换//
,避免在使用中转义,毕竟特殊符号出现的频率比/
低一些
- 查找
nginx
,替换为apache
[root@node-252 exercises]# sed -n '/nginx/p' passwd
nginx:x:997:995:Nginx web server:/var/lib/nginx:/sbin/nologin
[root@node-252 exercises]# sed -n 's#nginx#apache#gp' passwd
apache:x:997:995:Nginx web server:/var/lib/apache:/sbin/nologin
- 替换内容到文件
[root@node-252 exercises]# sed -i 's#nginx#apache#g' passwd
[root@node-252 exercises]# grep 'apache' passwd
apache:x:997:995:Nginx web server:/var/lib/apache:/sbin/nologin
这个过程不会有任何提示,所以使用需谨慎
一个错误示例,仅把匹配内容写到了文件
[root@node-252 exercises]# sed -i -n 's#nginx#apache#gp' passwd
[root@node-252 exercises]# cat passwd
apache:x:997:995:Nginx web server:/var/lib/apache:/sbin/nologin
所以,禁止-n
和-i
同时使用
- 替换内容到文件,并把原文件备份
[root@node-252 exercises]# sed -i.bak 's#apache#nginx#g' passwd
[root@node-252 exercises]# diff passwd passwd.bak
21c21
< nginx:x:997:995:Nginx web server:/var/lib/nginx:/sbin/nologin
---
> apache:x:997:995:Nginx web server:/var/lib/apache:/sbin/nologin
这里-i
后面可以加任何内容,当做备份文件的后缀名。但比较常用的例如.bak,.backup
,见名知意。
2.3 反向引用
一般格式:
sed -r 's#(.*)#\1#g'
因为()
属于扩展正则内容,所以想使用反向引用,那必须制定选项-r
- 找到nancy的命令解释器
[root@node-252 exercises]# sed -rn 's#^nancy.*:(.*)$#\1#gp' passwd
/bin/bash
- 使用sed找到本机ens33网卡的ip
[root@node-252 exercises]# ip a s ens33|sed -rn 's#.*inet (.*)/24.*#\1#gp'
192.168.202.128
我们一定要能定位到引用内容的位置,并用(.*)
表示
- 把匹配内容用
<>
括起来
[root@node-252 exercises]# echo 123456|sed -r 's#(.*)#<&>#g'
<123456>
如果匹配的是所有内容,可以使用&
表示前面所有的匹配内容
2.4 删除
一般格式:
sed '[定位]d' filename
只要不加-i
就不会修改文件
- 删除第1行
[root@node-252 exercises]# sed '1d' passwd|head -2
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
- 删除第1到3行
[root@node-252 exercises]# sed '1,3d' passwd|head -2
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
- 删除包含
root
和adm
的行
[root@node-252 exercises]# sed -r '/root|adm/d' passwd|head -2
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
- 删除包含
root
到adm
的行
[root@node-252 exercises]# sed -r '/root/,/adm/d' passwd|head -2
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
这部分和查找的使用方式基本类似
2.5 增加
一般格式:
sed '[定位]a' filename #append 追加
sed '[定位]i' filename #insert 插入
sed '[定位]c' filename #replace 替换
这里的替换,c
和前面的s
区别在于,c
替换整行内容,s
替换匹配内容
- 在第二行追加,插入,替换
hello sed
[root@node-252 exercises]# sed '2a hello sed' passwd|head -3
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
hello sed
[root@node-252 exercises]# sed '2i hello sed' passwd|head -3
root:x:0:0:root:/root:/bin/bash
hello sed
bin:x:1:1:bin:/bin:/sbin/nologin
[root@node-252 exercises]# sed '2c hello sed' passwd|head -3
root:x:0:0:root:/root:/bin/bash
hello sed
daemon:x:2:2:daemon:/sbin:/sbin/nologin
- 把
root
所在行追加,插入,替换为hello sed
[root@node-252 exercises]# sed '/root/a hello sed' passwd|head -3
root:x:0:0:root:/root:/bin/bash
hello sed
bin:x:1:1:bin:/bin:/sbin/nologin
[root@node-252 exercises]# sed '/root/i hello sed' passwd|head -3
hello sed
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
[root@node-252 exercises]# sed '/root/c hello sed' passwd|head -3
hello sed
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
- 把
root
到adm
行替换为hello sed
[root@node-252 exercises]# sed '/root/,/adm/c hello sed' passwd|head -3
hello sed
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
以上是sed的简单回顾,sed的强项是对行进行操作
3. awk 回顾
先记个通用格式吧
awk [options] '条件类型1 {动作1} [ 条件类型2 {动作2} ] ...' filename
3.1 取行
- 查找第一行
[root@node-252 exercises]# awk 'NR==1{print}' passwd
root:x:0:0:root:/root:/bin/bash
[root@node-252 exercises]# awk 'NR==1' passwd
root:x:0:0:root:/root:/bin/bash
print
是awk
的默认动作,会把匹配的内容进行打印,可以不写,但不能写的不完整,例如NR==1{}
或NR==1 print
都是不可以的
- 查找包含
root
和adm
的行
[root@node-252 exercises]# awk '/root|adm/' passwd
root:x:0:0:root:/root:/bin/bash
adm:x:3:4:adm:/var/adm:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
awk
默认支持扩展正则
- 查找第2到5行内容
[root@node-252 exercises]# awk 'NR>=2 && NR<=5' passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
[root@node-252 exercises]# awk 'NR==2,NR==5' passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
,
逗号在定位的时候基本上表示范围
- 查找第2和第5行内容
[root@node-252 exercises]# awk 'NR==2 || NR==5' passwd
bin:x:1:1:bin:/bin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
[root@node-252 exercises]# awk 'NR==2;NR==5' passwd
bin:x:1:1:bin:/bin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
- 查找除了第1行的内容
[root@node-252 exercises]# awk '!(NR==1)' passwd |head -2
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
awk
取行的关键字NR(Number of Records)
3.2 取列
为了观看方便,我们把passwd
只保留前5行
[root@node-252 exercises]# sed -i '6,$ d' passwd
[root@node-252 exercises]# cat passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
- 查找用户名和对应的UID
[root@node-252 exercises]# awk -F: '{print $1,$3}' passwd|column -t
root 0
bin 1
daemon 2
adm 3
lp 4
[root@node-252 exercises]# awk 'FS=":"{print $1,$3}' passwd
root:x:0:0:root:/root:/bin/bash
bin 1
daemon 2
adm 3
lp 4
-F
和FS
都可以指定分隔符
awk的FS设置如果不在BEGIN{}中设置,会导致第一行处理不正确。这是因为awk在处理文件时,如果需要在BEGIN{}之外设置FS(字段分隔符),则默认会认为是在寻找特定的模式(/pattern/),而没有对应的action(操作),因此会默认打印整行,导致第一行处理错误。为了正确设置FS或在awk脚本中执行其他需要在文件开始前设置的变量或操作,应该使用BEGIN{}来初始化这些变量。此外,如果想要跳过文件的第一行,可以使用条件NR>1来实现,其中NR表示当前处理的行号。这样,awk将处理从第二行开始的行,从而避免处理第一行时可能出现的错误
所以如果使用FS
应该在BEGIN
模块指定
[root@node-252 exercises]# awk 'BEGIN{FS=":"} {print $1,$3}' passwd
root 0
bin 1
daemon 2
adm 3
lp 4
- 取出内容并加上行号
[root@node-252 exercises]# awk -F: '{print NR,$0}' passwd
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
[root@node-252 exercises]# cat -n passwd
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
[root@node-252 exercises]# nl passwd
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
- 取出每行,并显示有多少列
[root@node-252 exercises]# awk -F: '{print $1,$3,NF}' passwd |column -t
root 0 7
bin 1 7
daemon 2 7
adm 3 7
lp 4 7
[root@node-252 exercises]# awk -F: '{print $1,$3,$NF}' passwd |column -t
root 0 /bin/bash
bin 1 /sbin/nologin
daemon 2 /sbin/nologin
adm 3 /sbin/nologin
lp 4 /sbin/nologin
awk
中的内置变量,在引用的时候,不需要加$
,否则会有歧义
- 取出UID以
1,2
开始的行
[root@node-252 exercises]# awk -F: '$3~/^[12]/' passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
$3~/***/
,~
波浪线代表包含的以上
- 取出UID不以
1,2
开始的行
[root@node-252 exercises]# awk -F: '$3!~/^[12]/' passwd
root:x:0:0:root:/root:/bin/bash
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
- 取出UID以
1,2
结尾的行
[root@node-252 exercises]# awk -F: '$3~/[12]$/' passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
- 取出UID以
1,2
结尾的行的第1,3列
[root@node-252 exercises]# awk -F: '$3~/[12]$/ {print $1,$3}' passwd
bin 1
daemon 2
awk
可以换一种思考方式:
条件 => 取行
动作 => 取列
上面的例子中:
条件为$3~/[12]$/
,取行为
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
动作为print $1,$3
,取列为
bin 1
daemon 2
3.3 BEGIN
执行顺序简述:
1. BEGIN{} : 最开始执行,读取文件之前
2. // : 正则
3. {} : 循环体
4. END{} : 最后执行,读完文件之后
记住BEGIN
是在读取文件前执行的操作,在BEGIN
中可以进行一些和文件内容无关的操作,比如定义分隔符,做一些运算等
AWK的BEGIN块主要用于在处理任何输入之前执行初始化操作,包括变量初始化、打印表头信息等。 在BEGIN块中,你可以执行各种计算和初始化操作,为后续的数据处理做好准备。例如,你可以在BEGIN块中设置初始值、定义变量,或者进行一些预处理计算。
BEGIN块的使用非常灵活,可以用于执行各种任务,包括但不限于:
- 初始化变量:在处理任何输入数据之前,你可以在BEGIN块中定义和初始化变量,这些变量可以在后续的代码中使用。例如,你可以设置一个总和变量为0,以便在处理数据时累加数值。
- 打印表头:如果你需要在输出中包含表头信息,可以在BEGIN块中打印这些信息。
- 执行预处理计算:在BEGIN块中,你还可以执行一些预处理计算,这些计算结果可以在后续的数据处理中使用。
通过使用BEGIN块,你可以确保在处理任何输入数据之前完成必要的初始化和准备工作,从而使你的AWK脚本更加健壮和可靠。此外,BEGIN块的执行是在处理任何输入数据之前,因此它提供了一个理想的位置来进行全局的设置和初始化操作
- 计算
1/3,1+3
[root@node-252 exercises]# awk 'BEGIN{print 1/3,1+3}'
0.333333 4
可以看到awk
中的BEGIN
模块支持计算,并且是浮点计算,这个功能要比expr
,bc
方便一些
- 找出UID小于3的账号
[root@node-252 exercises]# awk -F: 'BEGIN{min=3} $3<min {print $1,$3}' passwd
root 0
bin 1
daemon 2
需要注意在BEGIN
中定义的变量,在引用的时候不需要加$
,有点类似内置变量
- 找出本机中的非系统用户
[root@node-252 exercises]# awk -F: 'BEGIN{sys=999} $3>sys{print $1,$3}' /etc/passwd
nfsnobody 65534
yurq 1000
jacky 1001
nancy 1002
不同系统中的非系统用户的UID取值范围可能不同,参考/etc/login.defs
- 定义变量
[root@node-252 exercises]# awk 'BEGIN{FS=":"} {print $1,$3}' passwd
root 0
bin 1
daemon 2
adm 3
lp 4
3.4 END
END
在读取文件之后执行,所以可以进行一些统计工作
- 获得文件行数
[root@node-252 exercises]# awk -F: '{i++} END{print i}' passwd
5
[root@node-252 exercises]# awk -F: 'BEGIN{i=0} {i++} END{print i}' passwd
5
这里的i
如果未初始化,默认为0
,awk
读取每行文件,进行i++
计算,读取文件结束后,在END
模块进行打印
- 统计可登录用户数量
[root@node-252 exercises]# awk -F: '/bash$/{i++} END{print i}' /etc/passwd
4
[root@node-252 exercises]# grep 'bash' /etc/passwd
root:x:0:0:root:/root:/bin/bash
yurq:x:1000:1000:yurq:/home/yurq:/bin/bash
jacky:x:1001:1003::/home/jacky:/bin/bash
nancy:x:1002:1003::/home/nancy:/bin/bash
[root@node-252 exercises]# awk -F: '/bash$/{i++;print $1} END{print i}' /etc/passwd
root
yurq
jacky
nancy
4
命令解释器为bash
的用户认为是可登录用户,多个动作之间用;
分号隔离
- 统计
/etc/services
中的空行总数
[root@node-252 exercises]# awk '/^$/{c++;print c;print $0} END{print "total:" c}' /etc/services
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
total:17
当然,打印非空行
awk '!/^$/{c++;print c;print $0} END{print "total:" c}' /etc/services
- 假设
/etc/passwd
中的UID为流量,并且单位是MB
,统计一共用了多少GB
流量
[root@node-252 exercises]# awk -F: '{sum+=$3} END{print sum/1024 "GB"}' /etc/passwd
81.2568GB
3.5 gsub(替换功能)
gsub
是awk
中的一个内置函数,用于全局替换
一般格式:
gsub(regex, replacement, target)
- 把
root
替换成toor
[root@node-252 exercises]# awk 'gsub(/root/,"toor")' passwd
toor:x:0:0:toor:/toor:/bin/bash
精确到列
[root@node-252 exercises]# awk -F: 'gsub(/root/,"toor",$1)' passwd
toor x 0 0 root /root /bin/bash
- 把
passwd
中的数字删除
[root@node-252 exercises]# awk 'gsub(/[[:digit:]]/,"")' passwd
root:x:::root:/root:/bin/bash
bin:x:::bin:/bin:/sbin/nologin
daemon:x:::daemon:/sbin:/sbin/nologin
adm:x:::adm:/var/adm:/sbin/nologin
lp:x:::lp:/var/spool/lpd:/sbin/nologin
gsub
虽然能实现一些简单的替换功能,但是相较于sed
可能还是略显不足
4. 练习
正式练习现在才刚刚开始~~
以下示例以/etc/passwd
为练习文件,建议拷贝到其他目录进行使用。本文中的passwd
进行精简,节省篇幅
[root@node-252 exercises]# cat passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
- 删除所有数字
[root@node-252 exercises]# sed -r 's#[[:digit:]]+##g' passwd
root:x:::root:/root:/bin/bash
bin:x:::bin:/bin:/sbin/nologin
daemon:x:::daemon:/sbin:/sbin/nologin
adm:x:::adm:/var/adm:/sbin/nologin
lp:x:::lp:/var/spool/lpd:/sbin/nologin
[root@node-252 exercises]# sed -r 's#[0-9]+##g' passwd
root:x:::root:/root:/bin/bash
bin:x:::bin:/bin:/sbin/nologin
daemon:x:::daemon:/sbin:/sbin/nologin
adm:x:::adm:/var/adm:/sbin/nologin
lp:x:::lp:/var/spool/lpd:/sbin/nologin
- 删除每行开头空格
[root@node-252 exercises]# cat >> test <<eof
> 123
> 456
> 789;
> ==
> 200
> eof
[root@node-252 exercises]# cat -A test
123$
456$
789;$
==$
200$
[root@node-252 exercises]# sed -r 's#^ +##g' test
123
456
789;
==
200
- 用制表符替换空格
[root@node-252 exercises]# sed -r 's#^ +#\t#g' test
123
456
789;
==
200
- 所有数字用{}括起来
[root@node-252 exercises]# sed -r 's#([0-9]+)#{\1}#g' passwd
root:x:{0}:{0}:root:/root:/bin/bash
bin:x:{1}:{1}:bin:/bin:/sbin/nologin
daemon:x:{2}:{2}:daemon:/sbin:/sbin/nologin
adm:x:{3}:{4}:adm:/var/adm:/sbin/nologin
lp:x:{4}:{7}:lp:/var/spool/lpd:/sbin/nologin
[root@node-252 exercises]# sed -r 's#([0-9]+)#{&}#g' passwd
root:x:{0}:{0}:root:/root:/bin/bash
bin:x:{1}:{1}:bin:/bin:/sbin/nologin
daemon:x:{2}:{2}:daemon:/sbin:/sbin/nologin
adm:x:{3}:{4}:adm:/var/adm:/sbin/nologin
lp:x:{4}:{7}:lp:/var/spool/lpd:/sbin/nologin
- 删除文件中第一个字符
[root@node-252 exercises]# sed 's#^.##g' passwd
oot:x:0:0:root:/root:/bin/bash
in:x:1:1:bin:/bin:/sbin/nologin
aemon:x:2:2:daemon:/sbin:/sbin/nologin
dm:x:3:4:adm:/var/adm:/sbin/nologin
p:x:4:7:lp:/var/spool/lpd:/sbin/nologin
[root@node-252 exercises]# awk 'gsub(/^./,"")' passwd
oot:x:0:0:root:/root:/bin/bash
in:x:1:1:bin:/bin:/sbin/nologin
aemon:x:2:2:daemon:/sbin:/sbin/nologin
dm:x:3:4:adm:/var/adm:/sbin/nologin
p:x:4:7:lp:/var/spool/lpd:/sbin/nologin
- 删除第二个字符
[root@node-252 exercises]# sed -r 's#(.).(.*)#\1\2#g' passwd
rot:x:0:0:root:/root:/bin/bash
bn:x:1:1:bin:/bin:/sbin/nologin
demon:x:2:2:daemon:/sbin:/sbin/nologin
am:x:3:4:adm:/var/adm:/sbin/nologin
l:x:4:7:lp:/var/spool/lpd:/sbin/nologin
[root@node-252 exercises]# sed -r 's#^(.).#\1#g' passwd
rot:x:0:0:root:/root:/bin/bash
bn:x:1:1:bin:/bin:/sbin/nologin
demon:x:2:2:daemon:/sbin:/sbin/nologin
am:x:3:4:adm:/var/adm:/sbin/nologin
l:x:4:7:lp:/var/spool/lpd:/sbin/nologin
了解
[root@node-252 exercises]# awk '{print gensub(/^(.)./,"\\1","g")}' passwd
rot:x:0:0:root:/root:/bin/bash
bn:x:1:1:bin:/bin:/sbin/nologin
demon:x:2:2:daemon:/sbin:/sbin/nologin
am:x:3:4:adm:/var/adm:/sbin/nologin
l:x:4:7:lp:/var/spool/lpd:/sbin/nologin
- 删除最后一个字符
[root@node-252 exercises]# sed 's#.$##g' passwd
root:x:0:0:root:/root:/bin/bas
bin:x:1:1:bin:/bin:/sbin/nologi
daemon:x:2:2:daemon:/sbin:/sbin/nologi
adm:x:3:4:adm:/var/adm:/sbin/nologi
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologi
- 删除倒数第二个字符
[root@node-252 exercises]# sed -r 's#.(.)$#\1#g' passwd
root:x:0:0:root:/root:/bin/bah
bin:x:1:1:bin:/bin:/sbin/nologn
daemon:x:2:2:daemon:/sbin:/sbin/nologn
adm:x:3:4:adm:/var/adm:/sbin/nologn
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologn
- 删除第二个单词
这里我们认为连续的字母组成的字符串就是单词,此例中可以认为是
x
,但想表达的不是仅仅删x
#这里我们知道文件内容格式,所以这样写
[root@node-252 exercises]# sed -r 's#^([[:alpha:]]+:)[[:alpha:]]+#\1#g' passwd
root::0:0:root:/root:/bin/bash
bin::1:1:bin:/bin:/sbin/nologin
daemon::2:2:daemon:/sbin:/sbin/nologin
adm::3:4:adm:/var/adm:/sbin/nologin
lp::4:7:lp:/var/spool/lpd:/sbin/nologin
[root@node-252 exercises]# sed -r 's#(^[^:]+:)[^:]+#\1#g' passwd
root::0:0:root:/root:/bin/bash
bin::1:1:bin:/bin:/sbin/nologin
daemon::2:2:daemon:/sbin:/sbin/nologin
adm::3:4:adm:/var/adm:/sbin/nologin
lp::4:7:lp:/var/spool/lpd:/sbin/nologin
[root@node-252 exercises]# sed -r 's#(^[^:]+:).#\1#g' passwd #玩赖写法
root::0:0:root:/root:/bin/bash
bin::1:1:bin:/bin:/sbin/nologin
daemon::2:2:daemon:/sbin:/sbin/nologin
adm::3:4:adm:/var/adm:/sbin/nologin
lp::4:7:lp:/var/spool/lpd:/sbin/nologin
通用的删除第二个单词
[root@node-252 exercises]# head -10 /etc/services
# /etc/services:
# $Id: services,v 1.55 2013/04/14 ovasik Exp $
#
# Network services, Internet style
# IANA services version: last updated 2013-04-10
#
# Note that it is presently the policy of IANA to assign a single well-known
# port number for both TCP and UDP; hence, most entries here have two entries
# even if the protocol doesn't support UDP operations.
# Updated from RFC 1700, ``Assigned Numbers'' (October 1994). Not all ports
[root@node-252 exercises]# head -10 /etc/services|sed -r 's#([^[:alpha:]]*)([[:alpha:]]+)([^[:alpha:]]+)([[:alpha:]]+)#\1\2\3#g'
# /etc/:
# $Id: ,v 1.55 2013/04/14 Exp $
#
# Network , Internet
# IANA version: updated 2013-04-10
#
# Note it presently policy IANA assign single -known
# port for TCP UDP; , most here two
# even the doesn' support operations.
# Updated RFC 1700, `` Numbers'' ( 1994). Not ports
- 删除倒数第二个单词
#删除倒数第二段字符串
[root@node-252 exercises]# sed -r 's#[a-Z/]+:([a-Z/]+)$#\1#g' passwd
root:x:0:0:root:/bin/bash
bin:x:1:1:bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin/nologin
adm:x:3:4:adm:/sbin/nologin
lp:x:4:7:lp:/sbin/nologin
通用
[root@node-252 exercises]# sed -r 's#[[:alpha:]]+([^[:alpha:]]*)([[:alpha:]]+)([^[:alpha:]]*)$#\1\2\3#g' passwd
root:x:0:0:root:/root://bash
bin:x:1:1:bin:/bin://nologin
daemon:x:2:2:daemon:/sbin://nologin
adm:x:3:4:adm:/var/adm://nologin
lp:x:4:7:lp:/var/spool/lpd://nologin
- 删除最后一个单词
[root@node-252 exercises]# sed -r 's#[a-Z]+$##g' passwd
root:x:0:0:root:/root:/bin/
bin:x:1:1:bin:/bin:/sbin/
daemon:x:2:2:daemon:/sbin:/sbin/
adm:x:3:4:adm:/var/adm:/sbin/
lp:x:4:7:lp:/var/spool/lpd:/sbin/
- 交换每行第一个和第二个字符
[root@node-252 exercises]# sed -r 's#^(.)(.)#\2\1#g' passwd
orot:x:0:0:root:/root:/bin/bash
ibn:x:1:1:bin:/bin:/sbin/nologin
ademon:x:2:2:daemon:/sbin:/sbin/nologin
dam:x:3:4:adm:/var/adm:/sbin/nologin
pl:x:4:7:lp:/var/spool/lpd:/sbin/nologin
- 交换第一个字符和第二个单词
[root@node-252 exercises]# sed -r 's#^(.)([[:alpha:]]*)([^[:alpha:]]+)([[:alpha:]]+)#\4\2\3\1#g' passwd
xoot:r:0:0:root:/root:/bin/bash
xin:b:1:1:bin:/bin:/sbin/nologin
xaemon:d:2:2:daemon:/sbin:/sbin/nologin
xdm:a:3:4:adm:/var/adm:/sbin/nologin
xp:l:4:7:lp:/var/spool/lpd:/sbin/nologin
[root@node-252 exercises]# sed -r 's#^(.)([a-Z]+:)([a-Z]+)#\3\2\1#g' passwd
xoot:r:0:0:root:/root:/bin/bash
xin:b:1:1:bin:/bin:/sbin/nologin
xaemon:d:2:2:daemon:/sbin:/sbin/nologin
xdm:a:3:4:adm:/var/adm:/sbin/nologin
xp:l:4:7:lp:/var/spool/lpd:/sbin/nologin
[root@node-252 exercises]# head -10 /etc/services|sed -r 's#^(.)([[:alpha:]]*)([^[:alpha:]]+)([[:alpha:]]+)([^[:alpha:]]+)([[:alpha:]]+)#\6\2\3\4\5\1#g'
services /etc/#:
services $Id: #,v 1.55 2013/04/14 ovasik Exp $
#
services Network #, Internet style
services IANA # version: last updated 2013-04-10
#
that Note # it is presently the policy of IANA to assign a single well-known
number port # for both TCP and UDP; hence, most entries here have two entries
if even # the protocol doesn't support UDP operations.
from Updated # RFC 1700, ``Assigned Numbers'' (October 1994). Not all ports
[root@node-252 exercises]# head -10 /etc/services
# /etc/services:
# $Id: services,v 1.55 2013/04/14 ovasik Exp $
#
# Network services, Internet style
# IANA services version: last updated 2013-04-10
#
# Note that it is presently the policy of IANA to assign a single well-known
# port number for both TCP and UDP; hence, most entries here have two entries
# even if the protocol doesn't support UDP operations.
# Updated from RFC 1700, ``Assigned Numbers'' (October 1994). Not all ports
- 交换第一个单词和最后一个单词
[root@node-252 exercises]# sed -r 's#^([[:alpha:]]+)(.*/)([[:alpha:]]+)$#\3\2\1#g' passwd
bash:x:0:0:root:/root:/bin/root
nologin:x:1:1:bin:/bin:/sbin/bin
nologin:x:2:2:daemon:/sbin:/sbin/daemon
nologin:x:3:4:adm:/var/adm:/sbin/adm
nologin:x:4:7:lp:/var/spool/lpd:/sbin/lp
- 每行打印3次
[root@node-252 exercises]# sed 'p;p' passwd
root:x:0:0:root:/root:/bin/bash
root:x:0:0:root:/root:/bin/bash
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
adm: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
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
默认打印一次,每多加个
p
多打印一次
- 隔行删除
[root@node-252 exercises]# seq 10|sed '1~2d'
2
4
6
8
10
[root@node-252 exercises]# seq 10|sed '2~2d'
1
3
5
7
9
[root@node-252 exercises]# seq 10|sed -n '1~2p'
1
3
5
7
9
- 仅显示第一列
[root@node-252 exercises]# grep -E -o '^[a-Z]+' passwd
root
bin
daemon
adm
lp
[root@node-252 exercises]# awk -F: '{print $1}' passwd
root
bin
daemon
adm
lp
[root@node-252 exercises]# sed -r 's#^([[:alpha:]]+).*#\1#g' passwd
root
bin
daemon
adm
lp
可以看出
awk
处理列内容更便捷
本次就这些 ^_^