三剑客-awk
三剑客-awk
[TOC]
awk
概述

每个的特点不同,有一定的差异
格式


- 也是一行一行的读,没有默认输出
- 直到读到文件的最后一行就完事了
- 默认支持扩展正则
1)⭐取行

- NR是awk的内置变量,存储着每行的行号
- $0 表示当前行的内容 也就是整行输出




模糊匹配
📌 awk语法:
awk '/正则/' file- awk默认支持扩展正则
🌰 案例:输出包含root或者adm的行
[root@oldboyedu ~]# awk '/root|adm/' passwd.txtroot:x:0:0:root:/root:/bin/bashadm:x:3:4:adm:/var/adm:/sbin/nologinoperator:x:11:0:operator:/root:/sbin/nologin

🌰 案例:区间范围
[root@oldboyedu ~]# awk '/root/,/adm/' passwd.txtroot:x:0:0:root:/root:/bin/bashbin:x:1:1:bin:/bin:/sbin/nologindaemon:x:2:2:daemon:/sbin:/sbin/nologinadm:x:3:4:adm:/var/adm:/sbin/nologinoperator:x:11:0:operator:/root:/sbin/nologin2)⭐取列


- 取倒数第二列:
$(NF-1) - 这两列是没有任何关系的,单独的两列
- 别忘了{ }里面加 print
- 2 文件中的第二列
,逗号awk内置变量,存储着空格'{print $4,$NF}'- 认识双引号,里面的内容原样输出
'{print $4" "$NF}'- 双引号取代了原本的,逗号


输出对齐

[root@oldboyedu ~]# ll | awk '{print $4,$NF}' | column -t# column -t 实现对齐输出[root@oldboyedu ~]# ll | awk '{print $4"\t"$NF}'# \t 相当于是取代了,逗号- 别忘了中间的双引号
"\t"相当于是取代了,逗号


” ” 双引号
- 输出自定义内容
- ” ” 双引号里面的东西都可以被输出

- 原理同上,双引号取代了原本的,逗号
- 双引号里面的内容被原样输出
[root@oldboyedu ~]# cat 4.txt1.txt2.txt3.txt
[root@oldboyedu ~]# awk '{print $1}' 4.txt1.txt2.txt3.txt
[root@oldboyedu ~]# awk '{print "touch "$1}' 4.txttouch 1.txttouch 2.txttouch 3.txt
[root@oldboyedu ~]# awk '{print "touch "$1";cd /etc"}' 4.txttouch 1.txt;cd /etctouch 2.txt;cd /etctouch 3.txt;cd /etc⭐awk -F (指定分隔符)

- 空白字符:空格,连续的空格,tab键
- -F 和分隔符 ’ : ’ 之间不留空格也是可以的
- -F后面匹配的字符可以用正则表达式


可是我们不要后面的 / 斜杠,那怎么办?
因为我们知道-F后面的分隔符是可以用正则表达式的,所以我们可以匹配不同的符号,从而进行分割
[root@oldboyedu ~]# awk -F '[ /]' file# 中括号,这几个字符中的一种,空格或/ 斜杠
- 这里是以 空格 或者 / 任意一个字符为分隔符

可以明显感觉,还是很麻烦,怎么让连续的空格变为一个呢,+ #连续出现一次及以上
[root@oldboyedu ~]# awk -F '[ /]+' file# 这样就可以了⚠️ 特别要注意这个+号,因为有时候它前面的空格不止一个,而且他们还都是连续的空格,所以很多时候这个+号是很有必要的

📌 这个+号是很有必要的
passwd.txt也是同理
-F':'#只以冒号进行分隔(不彻底)-F'[:/]'#只要匹配到了其中一个字符就会切一刀,切的刀数太频繁了-F'[:/]+'#后面来一个**+号**,完美解决这个问题!
🌰 案例:取出test字符串
[root@oldboyedu ~]# echo -+=:/oldboy_=+-:test-/-=oldgirl | awk -F "[-+=:/_]" '{print $11}'test# 没加+号,分隔符匹配次数多,test在第11列
[root@oldboyedu ~]# echo -+=:/oldboy_=+-:test-/-=oldgirl | awk -F "[-+=:/_]+" '{print $3}'test# 加了+号,test在第3列
'匹配过程'[root@oldboyedu ~]# echo -+=:/oldboy_=+-:test-/-=oldgirl | egrep -o "[-+=:/_]"-+=:/_=# 没加+号,每个字符单独匹配
[root@oldboyedu ~]# echo -+=:/oldboy_=+-:test-/-=oldgirl | egrep -o "[-+=:/_]+"-+=:/_=+-:-/-=# 加了+号,连续的符号视为一个整体
- 如果上面的理解了,那么这个也难不倒你吧
- 切两刀,这个IP地址在第②列
取列小结


- 默认连续空格和加了-F的,后面取的列数是不一样的
- 上图是取inet这里一列
- 如果是取IP地址呢?
- 一个3(
[ /]+),不一样
总结来说,就是如果是默认的情况,开头即使有一些连续的字符什么的,他都给忽略了
3)⭐取行+取列

我们刚才在用取行的时候,默认只用了**“条件”;而我们在取列的时候默认只用了”动作”,现在我们就中和**一下他们

📌 语法结构:
awk '找谁{干啥}' file# 直接指定行数
awk '/找谁/{干啥}' file# 利用正则模糊匹配

🌰 案例:输出文件大于3小于6的行的第一列和最后一列
[root@oldboyedu ~]# awk 'NR>3&&NR<6' passwd.txtadm:x:3:4:adm:/var/adm:/sbin/nologinlp:x:4:7:lp:/var/spool/lpd:/sbin/nologin# 这里只取行
[root@oldboyedu ~]# awk -F':' 'NR>3&&NR<6{print $1,$NF}' passwd.txtadm /sbin/nologinlp /sbin/nologin# 取行的同时 -F指定分隔符,打印指定列数🌰 案例:找出包含root的行,输出最后一列内容
[root@oldboyedu ~]# awk -F':' '/root/{print $NF}' passwd.txt/bin/bash/sbin/nologin🌰 案例:取出当前磁盘的使用率是多少
[root@oldboyedu ~]# df -h | awk 'NR==6'/dev/mapper/klas-root 47G 3.5G 44G 8% /[root@oldboyedu ~]# df -h | awk 'NR==6{print $(NF-1)}'8%# 取倒数第二列或者
[root@oldboyedu ~]# df -h | awk '/\/$/'/dev/mapper/klas-root 47G 3.5G 44G 8% /# 匹配以/结尾的行,用撬棍把第二个/打回原形!'这个思想很巧妙'
[root@oldboyedu ~]# df -h | awk '/\/$/{print $(NF-1)}'8%# 也是倒数第二列📌 面试题:取出文件中的第5行的第3列
[root@oldboyedu ~]# awk 'NR==5{print $3}' file'so easy 啦!'⭐数字比较
📌 语法格式:
awk '$3==0' fileawk '$3>5' file如果某列是数字,可以做以下比较输出
== > >= < <= !=
🌰 案例:取出第3列等于0的行
[root@oldboyedu ~]# awk -F':' '$3==0' passwd.txtroot:x:0:0:root:/root:/bin/bash# 得有这-F指定分隔符🌰 案例:取出第三列大于5的行
[root@oldboyedu ~]# awk -F':' '$3>5' passwd.txtshutdown:x:6:0:shutdown:/sbin:/sbin/shutdownhalt:x:7:0:halt:/sbin:/sbin/haltmail:x:8:12:mail:/var/spool/mail:/sbin/nologinoperator:x:11:0:operator:/root:/sbin/nologin🌰 案例:取出第三列大于3并且小于6的行
[root@oldboyedu ~]# awk -F':' '$3>3&&$3<6' passwd.txtlp:x:4:7:lp:/var/spool/lpd:/sbin/nologinsync:x:5:0:sync:/sbin:/bin/sync
[root@oldboyedu ~]# awk -F':' '$3>3&&$3<6' passwd.txt | column -t# column -t 对齐
💝妙用

- 很新颖!
- 后面不打印列数,直接用” “打印想要输出的话

加行数
很少用(每一行加一)
📌 类似于 wc -l统计行数,但是你得了解END的使用

- i初值为0,后面加的是1,相当于每行加1

BEGIN: 在读取文件之前做的动作 END:在读取文件之后做的动作
也就END用的多一点


这个案例后期,可以搭配各种条件使用

⭐加列数
$行数(加上每一列的具体的值)


- i的初值为0
- 前面可以跟过滤条件
//进行限制

字符串匹配
⚠️ **注意:**用双引号将字符串包裹起来
输出第一列等于root的行
[root@oldboyedu ~]# awk -F':' '$1=="root"' passwd.txtroot:x:0:0:root:/root:/bin/bash输出第一列不等于root的行
[root@oldboyedu ~]# awk -F':' '$1!="root"' passwd.txtbin:x:1:1:bin:/bin:/sbin/nologindaemon:x:2:2:daemon:/sbin:/sbin/nologinadm:x:3:4:adm:/var/adm:/sbin/nologin输出最后一列等于/sbin/halt的行
[root@oldboyedu ~]# awk -F':' '$NF=="/sbin/halt"' passwd.txthalt:x:7:0:halt:/sbin:/sbin/halt⭐~ (波浪线)


📌 匹配第6列以v开头的行
[root@oldboyedu ~]# awk -F "[:/]+" '$6 ~ /^v/' passwd.txtadm:x:3:4:adm:/var/adm:/sbin/nologinlp:x:4:7:lp:/var/spool/lpd:/sbin/nologinmail:x:8:12:mail:/var/spool/mail:/sbin/nologin🌰 案例:第四列以0或者1开头的行
- 没有
$~就是对整个行进行正则匹配,而不是单独的某一列

02.三剑客习题

[root@oldboyedu ~]# egrep -i 'oldboy' file# egrep -i 忽略大小写


- 下图的点为任意一个字符

- 加上撬棍变成普通的点


sed命令实现:
- 反向引用

- 因为贪心算法,匹配到最后一个:

egrep命令实现:## 难点在于写出用户名的表达式


取最后一行


xargs查找/etc/下面所有文件中的一句话
⚠️ 审题:所有文件,不是下面一层文件,所以find命令中没有 -maxdepth 1 这个限制
**场景:**电脑中病毒,每个文件中都有这一句

反引号 和 $() ,都是取里面的命令并执行


[root@oldboyedu ~]# find /etc/ -type f | xargs grep 'linux'# xargs grep 'linux'
删除空行




典型工作流示例
📌 分析网站访问日志:统计IP访问量并排序
[root@oldboyedu ~]# awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10 1428 192.168.1.23 1205 10.0.0.45 983 172.16.5.201# ... (TOP 10 IPs)统计每一个字符(单词)出现的次数,并取出次数最多的前10位
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!




