grep
-E
:相当于 egrep,支持扩展正则。-A
:匹配过滤的内容以及下面的行,例如下面5行为-A5
。-B
:匹配过滤的内容以及上面的行,例如上面5行为-B5
。-C
:匹配过滤的内容以及上下的行,例如上下5行为-C5
。-c
:统计出现了多少行,类似wc -l
。-v
:显示除过滤行以外的其他行内容。-n
:显示行号。-i
:忽略大小写。-w
:精确过滤。
精确过滤的几种方法(只匹配 abc):
echo abc abcde abcdef abcc | grep -w abc
echo abc abcde abcdef abcc | grep '\babc\b'
echo abc abcde abcdef abcc | grep '\<abc\>'
sed
查找
'1p' '2p'
:指定行号进行查找。'1,5p'
:指定行号范围进行查找。/word/p
:类似于 grep 过滤,支持正则,扩展正则需加上-r
参数。/10:00/,/11:00/p
:按照指定范围进行过滤。如果查找不到结束关键字,则输出到最后一行。
注意:使用时需加上-n
参数,否则 sed 默认输出所有内容。
# 查找第5行内容
sed -n '5p' 2_test.txt
# 查找第5-9行内容
sed -n '5,9p' 2_test.txt
# 查找从第5行开始到最后一行
sed -n '5,$p' 2_test.txt
# 查找最后一行内容
sed -n '$p' 2_test.txt
# 查找含有 demo 的行
sed -n '/demo/p' 2_test.txt
# 支持正则查找
sed -n '/[ab]/p' 2_test.txt
sed -nr '/s?t$/p' 2_test.txt
# 范围
sed -nr '/apple/,/demo/p' 2_test.txt
删除
'1d'
:指定行号进行删除。'1,5d'
:指定行号范围进行删除。/word/d
:类似于 grep 过滤,支持正则,扩展正则需加上-r
参数。/10:00/,/11:00/d
:按照指定范围进行删除。
# 删除第2-5行内容
$ sed '2,5d' 2_test.txt
1 apple
6 demo
7 test
8 hhst
9
10
# 支持正则表达式
$ sed -r '/t$/d' 2_test.txt
1 apple
2 apple
4 amazon
5 twitter
6 demo
9
10
增加
c
:替换指定行内容。a
:append,追加,向指定的行或每一行追加内容(行后面)。i
:insert,插入,向指定的行或每一行插入内容(行前面)。
# 行后增加
$ sed '3a insert content' 2_test.txt
1 apple
2 apple
3 microsoft
insert content
4 amazon
# 行前增加
$ sed '3i insert content' 2_test.txt
1 apple
2 apple
insert content
3 microsoft
4 amazon
# 替换
$ sed '3c insert content' 2_test.txt
1 apple
2 apple
insert content
4 amazon
替换
s
:substitute 替换,sed默认只替换每行第一个匹配的内容。g
:global 全局替换,替换每行所有的匹配内容。
使用方法:s###g
、s///g
、[email protected]@@g
# 待处理文本内容
$ cat 2_test.txt
01 apple
02 apple
03 microsoft
04 amazon
05 twitter
06 demo
07 test
08 hhst
09
10
# 将数字替换为空
$ sed 's#[0-9]##g' 2_test.txt
apple
apple
microsoft
amazon
twitter
demo
# 不加 g 的情况
$ sed 's#[0-9]##' 2_test.txt
1 apple
2 apple
3 microsoft
4 amazon
5 twitter
6 demo
7 test
8 hhst
9
0
后项引用,反向引用。
# 给 123 头尾加上尖括号,\1 表示第一对小括号
$ echo '123' | sed -r 's#(.*)#<\1>#g'
<123>
# 交换下划线前后的内容
$ echo 'hello_world' | sed -r 's#(^.*)_(.*$)#\2_\1#g'
world_hello
实例:过滤 IP 地址
# 未过滤
$ ifconfig ens33
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.16.166.161 netmask 255.255.255.0 broadcast 172.16.166.255
# 通过 sed 过滤
$ ifconfig ens33 | sed -n '2p' | sed -r 's#^.*inet (.*) net.*$#\1#g'
172.16.166.161
# 简化
$ ifconfig ens33 | sed -nr '2s#^.*inet (.*) net.*$#\1#gp'
172.16.166.161
实例:过滤权限
# 未过滤
$ stat 2_test.txt
文件:2_test.txt
大小:82 块:8 IO 块:4096 普通文件
设备:10302h/66306d Inode:396927 硬链接:1
权限:(0664/-rw-rw-r--) Uid:( 1000/suzukaze) Gid:( 1000/suzukaze)
# 过滤后
$ stat 2_test.txt | sed -nr '4s#^.*\(0(.*)/-.*$#\1#gp'
664
# 一个小坑,在过滤之前先查看该命令自身是否有提供过滤选项,别急着正则过滤处理
$ stat -c%a 2_test.txt
664
awk
内置变量、参数
NR
:Number of Record 记录号(行号)NF
:Number of Fields 每行的字段数(列数)-v FS
:Field Separator 字段分隔符,相当于-F
-v OFS
:Output Field Separator 输出字段分隔符
行
NR==1
:取出指定行。NR>=1 && NR<=5
:取出指定范围行。/word/
:取出包含关键字的行。/word1/,/word2/
:取出包含关键字范围的行。如果查找不到结束关键字,则输出到最后一行。
$ awk 'NR==5' 2_test.txt
05 twitter
$ awk '/ap/,/tes/' 2_test.txt
01 apple
02 apple
03 microsoft
04 amazon
05 twitter
06 demo
07 test
列
使用-F
指定分隔符,默认为空格(包括连续的空格和tab)。
$n
:取出第n列(n>0)。$0
:取出整行内容。$NF
:取出最后一列。
# 取出第5列和第8列
$ ll | awk '{print $5,$8}'
4096 14:07
4096 19:40
95 19:03
82 14:07
4096 19:14
4096 19:14
# column -t 有对齐效果
$ ll | awk '{print $5,$8}' | column -t
4096 14:07
4096 19:40
95 19:03
82 14:07
4096 19:14
4096 19:14
# 从文件中取列,指定冒号为分隔符
$ awk -F: '{print $1,$NF}' /etc/passwd
root /bin/bash
daemon /usr/sbin/nologin
bin /usr/sbin/nologin
sys /usr/sbin/nologin
sync /bin/sync
games /usr/sbin/nologin
man /usr/sbin/nologin
# 使用双引号自定义内容
$ awk -F: '{print $1"---"$NF}' /etc/passwd
root---/bin/bash
daemon---/usr/sbin/nologin
bin---/usr/sbin/nologin
# 自定义分隔符,-vOFS == -v OFS
$ awk -F: -vOFS=: '{print $1,$NF}' /etc/passwd
root:/bin/bash
daemon:/usr/sbin/nologin
bin:/usr/sbin/nologin
sys:/usr/sbin/nologin
实例:过滤 IP 地址
# 过滤前
$ ip address show ens33
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:0c:29:99:ea:24 brd ff:ff:ff:ff:ff:ff
altname enp2s1
inet 172.16.166.161/24 brd 172.16.166.255 scope global dynamic noprefixroute ens33
valid_lft 1044sec preferred_lft 1044sec
inet6 fe80::abe6:a1ef:a294:b07d/64 scope link noprefixroute
valid_lft forever preferred_lft forever
# 过滤后,分隔符可以使用正则表达式
$ ip a s ens33 | awk -F"[ /]+" 'NR==4{print $3}'
172.16.166.161
模式匹配
格式:pattern {action}
A pattern can be:
BEGIN
END
expression
expression , expression
正则
~
:匹配。!~
:不匹配。
# 第3列以2开头的行
$ awk -F: '$3~/^2/' /etc/passwd
bin:x:2:2:bin:/bin:/usr/sbin/nologin
# 第3列以2开头,打印第1列和最后1列
$ awk -F: '$3~/^2/{print $1,$NF}' /etc/passwd
bin /usr/sbin/nologin
范围
//,//
NR==1,NR==5
$ awk -F: '/120/,/200/{print $1}' /etc/passwd
nm-openvpn
saned
colord
geoclue
pulse
特殊模式
BEGIN
:在awk读取文件之前执行。END
:在awk读取文件之后执行。
# 统计空行,效果类似 wc -l
$ awk '/^$/{i++}END{print i}' /etc/services
6
# 从1累加到100
$ seq 100 | awk '{sum=sum+$1}END{print sum}'
5050
# 使用分号隔开多个表达式
$ seq 5 | awk '{sum=sum+$1; print sum}END{print "total",sum}'
1
3
6
10
15
total 15
数组
- 赋值:
array[0]="hello"
,字符串需要使用双引号,否则 awk 会识别为变量,数字可以不用添加双引号。 - 循环:
for(i in array)
,此处的i
为下标。
# 赋值
$ awk 'BEGIN{list[0]="hello";list[1]="awk";list[2]=123;print list[0],list[1],list[2]}'
hello awk 123
# 循环输出
$ awk 'BEGIN{list[0]="awk"; list[1]="hey"; list[2]=123; for(i in list)print i,list[i]}'
2 123
0 awk
1 hey
实例:统计域名出现次数
# 文件内容如下
https://www.apple.com/1.html
https://api.apple.com/2.html
https://api.apple.com/index.html
https://www.apple.com/12.html
https://download.apple.com/index.html
https://www.apple.com/iphone.html
# 统计结果
$ awk -F"[./]+" '{array[$2]++}END{for(i in array)print i,array[i]}' 03_url.txt
api 2
www 3
download 1
# 排序
$ awk -F"[./]+" '{array[$2]++}END{for(i in array)print i,array[i]}' 03_url.txt | sort -rnk2
www 3
api 2
download 1
for 循环
见例子:
# 计算1-100累加
$ awk 'BEGIN{for(i=1;i<=100;i++)sum+=i; print sum}'
5050
if 判断
见例子:
# 打印磁盘使用率大于1%的挂载点
$ df -h | awk -F"[ %]+" 'NR>1{if($5>1)print $6,"disk is not enough"}'
/ disk is not enough
/boot/efi disk is not enough