AWK是一种处理文本文件的语言,是一个强大的文本分析工具,之因此叫AWK是由于其取了三位创始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符。java
$ awk 'BEGIN{}pattern{commands}END{}' file
复制代码
$ standard output | 'BEGIN{}pattern{commands}END{}'
复制代码
其中数字1,2,3…表明是文本中的数据行python
BEGIN{} 表示在处理文本数据以前进行一些操做正则表达式
pattern{commands} 表示从文本中的第一行开始匹配知足pattern规则的行,执行commands命令,直到文本最后一行spring
END{} 表示在处理文本数据以后进行一些操做shell
内置变量 | 含义 |
---|---|
$0 | 整行内容 |
![]() |
当前行的第1-n个字段 |
NF | 当前行的字段个数,也就是有多少列 |
NR | 当前的行号,从1开始计数 |
FNR | 各文件分别计数的行号 |
FS | 字段分隔符(默认以空格或tab键分割) |
RS | 输入行分隔符。默认回车换行 |
OFS | 输出字段分隔符。默认为空格 |
ORS | 输出行分隔符。默认为回车换行 |
FILENAME | 当前文件名 |
内置变量如何使用,咱们以文本score.txt内容来作演示vim
$ cat score.txt
marry 90 49 23 14
join 44 55 66 24
smile 55 78 80 34
jack 20 51 66 44
复制代码
使用$0
来打印输出文件的每一整行的内容数组
$ awk '{print $0}' score.txt
marry 90 49 23 14
join 44 55 66 24
smile 55 78 80 34
jack 20 51 66 44
复制代码
注意:行匹配语句 awk ' ' 只能用单引号bash
使用$1
、$3
来打印输出文件的的每一行第1列和第3列内容ssh
$ awk '{print $1,$3}' score.txt
marry 49
join 55
smile 78
jack 51
复制代码
使用NF
来输出每一行的列数tcp
$ awk '{print NF}' score.txt
5
5
5
5
复制代码
使用awk没有指定分隔符,默认是以空格或者tab键来做分隔,在score.txt 文件中,通过分隔,每行都有5列
使用变量$NF
来输出文件
$ awk '{print $NF}' score.txt
14
24
34
44
复制代码
从输出结果咱们总结出,
NF = $5
以前咱们打印每一行的第1列和第3列咱们使用的是:$1,(NF-2),$(NF-4)**
$ awk '{print $(NF-4),$(NF-2)}' score.txt
marry 49
join 55
smile 78
jack 51
复制代码
使用NR
来输出每一行的行号,此次咱们把每一行的内容也打印出来
$ awk '{print NR "\t" $0}' score.txt
1 marry 90 49 23 85
2 join 44 55 66 22
3 smile 55 78 80 24
4 jack 20 51 66 89
复制代码
使用ORS
来指定输出每一行的指定的分隔符
$ awk 'BEGIN{ORS="---"} {print $1,$NF}' score.txt
marry 14---join 24---smile 34---jack 44---
复制代码
指定了每一行的分隔符为"---",如今输出的每一行都用---作分隔,而不在是默认的回车换行作分隔
使用ORS
指定了行的分隔符为"---",可是如今咱们看到每一行输出的分隔符仍是默认的空格来作分隔,好比:输出第一行的 marry 14
,如今咱们使用OFS
来指定输出字段的分隔符,好比:*
号
$ awk 'BEGIN{ORS="---";OFS="*"} {print $1,$NF}' score.txt
marry*14---join*24---smile*34---jack*44---
复制代码
使用FS
指定每一行的字段分隔符
$ awk 'BEGIN{FS=":"} {print $1,$NF}' /etc/passwd
root /bin/bash
bin /sbin/nologin
复制代码
也可使用这种形式指定分隔符(-F的意思就是指定分隔符)
$ awk -F: '{print $1,$NF}' /etc/passwd
复制代码
若是指定多个分隔符,使用中括号[]
包起来,里面指定具体的分隔符
$ awk -F '[-:]'
复制代码
使用FILENAME
在处理多文件时候,能够打印出当前的文件名
$ awk 'BEGIN{FS="[ :]"} {print FILENAME "\t" $1,$NF}' score.txt /etc/passwd
score.txt marry 14
score.txt join 24
score.txt smile 34
score.txt jack 44
/etc/passwd root /bin/bash
/etc/passwd bin /sbin/nologin
复制代码
准备文件 netstat.txt
$ cat netstat.txt
Proto Recv-Q Send-Q Local-Address Foreign-Address State
tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN
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.10.10:22 192.168.10.1:65016 ESTABLISHED
tcp 0 0 192.168.10.10:22 192.168.10.1:57253 ESTABLISHED
tcp6 0 0 :::111 :::* LISTEN
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 ::1:25 :::* LISTEN
udp 0 0 0.0.0.0:1018 0.0.0.0:* FIN_WAIT2
udp6 0 0 :::1018 :::* FIN_WAIT2
udp6 0 0 :::111 :::* ESTABLISHED
udp6 0 0 ::1:323 :::* ESTABLISHED
复制代码
以空格为分隔符,按照$6
(第6列)也就是State
列进行拆分文件netstat.txt
(其中NR!=1表示不处理表头)
$ awk 'NR!=1{print > $6}' netstat.txt
$ ls
ESTABLISHED FIN_WAIT2 LISTEN netstat.txt
$ cat ESTABLISHED
tcp 0 0 192.168.10.10:22 192.168.10.1:65016 ESTABLISHED
tcp 0 0 192.168.10.10:22 192.168.10.1:57253 ESTABLISHED
udp6 0 0 :::111 :::* ESTABLISHED
udp6 0 0 ::1:323 :::* ESTABLISHED
$ cat FIN_WAIT2
udp 0 0 0.0.0.0:1018 0.0.0.0:* FIN_WAIT2
udp6 0 0 :::1018 :::* FIN_WAIT2
$ cat LISTEN
tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN
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
tcp6 0 0 :::111 :::* LISTEN
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 ::1:25 :::* LISTEN
复制代码
{print > $6}
是按照第6列进行拆分,将全部列输出到文件中,咱们也能够指定列输出到文件中
$ awk 'NR!=1{print $1,$4> $6}' netstat.txt
复制代码
printf
格式化输出printf
格式说明符
格式符 | 含义 |
---|---|
%s | 打印字符串 |
%d | 打印十进制数 |
%f | 打印一个浮点数 |
%x | 打印十六进制数 |
%o | 打印八进制数 |
%e | 打印数字的科学计数法形式 |
%c | 打印单个字符的ASCII码 |
printf
修饰符
修饰符 | 含义 |
---|---|
- | 左对齐 |
+ | 右对齐 |
# | 8进制显示在前面加0,16进制显示在前面加0X |
上面演示的案例,咱们都是用print
来作输出(默认是输出每行内容,用回车键做为换行),如今咱们使用printf
(默认没有加任何的输出分隔符)来进行格式化打印输出
使用%s
以字符串形式输出/etc/passwd文件中第一列
$ awk -F: '{printf "%s\n",$1}' /etc/passwd
root
bin
daemon
...
复制代码
使用
\n
来指定输出每行的的分隔符
使用$s
、%f
、%d
、%x
等多个格式符来输出
$ awk -F: '{printf "%s %f %d %x\n",$1,$3,$3,$3}' /etc/passwd
sshd 74.000000 74 4a
postfix 89.000000 89 59
chrony 998.000000 998 3e6
vagrant 1000.000000 1000 3e8
vboxadd 997.000000 997 3e5
复制代码
使用了不一样的格式符,输出了/etc/passwd文件中的$1,$3列,对于稍微有点强迫症患者的朋友来讲,能够看出输出结果并不美观,好比:每列都没有对齐(向左或者向右),浮点数列没有控制指定个数的精度等,下面咱们就来解决这个问题
$ awk -F: '{printf "%-20s %-0.2f \t\t %-20d %-20x\n",$1,$3,$3,$3}' /etc/passwd
sshd 74.00 74 4a
postfix 89.00 89 59
chrony 998.00 998 3e6
vagrant 1000.00 1000 3e8
vboxadd 997.00 997 3e5
复制代码
咱们能够在每一个格式符的前面加上数字,好比:%20s,表示这一列占位20个字符
使用 %0.2f 来指定这一列输出的精度,保留2位小数
使用
-
格式符,表示输出的结果向左对齐
运算符 | 含义 |
---|---|
<、 <=、 >、 >=、 != 、 == | 小于、小于等于...(关系运算符) |
~ | 匹配正则表达式 |
~! | 不匹配正则表达式 |
|| | 逻辑或 |
&& | 逻辑与 |
! | 非 |
匹配输出第一列字符等于smile
的行
$ awk '$1=="smile" {print $0}' score.txt
smile 55 78 80 34
复制代码
匹配输出第一列字符等于smile
的行 ,或者第2列大于 80
的行
$ awk '$1=="smile" || $2 > 80 {print $0}' score.txt
marry 90 49 23 14
smile 55 78 80 34
复制代码
使用正则,匹配输出包含44
的行
$ awk '/44/ {print $0}' score.txt
join 44 55 66 24
jack 20 51 66 44
复制代码
使用正则,匹配输出不包含44
的行
$ awk '!/44/ {print $0}' score.txt
marry 90 49 23 14
smile 55 78 80 34
复制代码
使用正则,匹配输出以join
字符开头的行到以jack
字符开头结束的行
$ awk '/^join/,/^jack/ {print $0}' score.txt
join 44 55 66 24
smile 55 78 80 34
jack 20 51 66 44
复制代码
使用正则,匹配第1列字符包含jack
而且最后一列大于等于34
$ awk '$1 ~ /jack/ && $NF >= 34 {print $0}' score.txt
jack 20 51 66 44
复制代码
~ 表示模式开始;/ / 中表示的是匹配的具体模式
如今看下正则模式取反的示例
$ awk '$1 !~ /jack/ && $NF >= 24 {print $0}' score.txt
join 44 55 66 24
smile 55 78 80 34
复制代码
!~
,第1列不包含字符jack
而且最后列的值大于等于24
运算符 | 含义 |
---|---|
+ 、 - | 加、减 |
* 、/ 、 % | 乘、除、求于 |
++、-- | 增长或减小,做为前缀或后缀,如(++x、x--) |
咱们先来看一下简单的加法和除法运算
$ awk 'BEGIN{ num1=10; num2=20; printf "%d %0.2f\n", num1+num2,num1/num2 }'
30 0.50
复制代码
awk 声明的变量能够在任何多个花括号脚本中使用
计算/etc/services中空白行的数量
$ awk '/^$/ {sum++} END{print sum}' /etc/services
17
复制代码
函数名 | 含义 |
---|---|
length(str) | 计算字符串长度 |
index(str1,str2) | 在str1中查找str2的位置 |
tolower(str) | 转换为小写 |
toupper(str) | 转换为大写 |
substr(str,m,n) | 从str的第m个字符开始,截取m位 |
split(str,arr,fs) | 按fs切割字符,结果保存arr中 |
match(str,RE) | 在str中按照RE查找 |
因为字符串函数是咱们工做中经常使用的函数,比较简单,下面咱们使用split()
函数来作演示
$ awk 'BEGIN{ str="java#python#go"; split(str,arr,"#"); for(item in arr){ print arr[item] } }'
java
python
go
复制代码
其中咱们使用了for循环来遍历 arr 数组,下面咱们会讲到
条件表达式
语法格式以下:
if(条件){
动做
}
复制代码
示例:
$ awk -F: '{if($3>100 && $3<1000) print $0}' /etc/passwd
复制代码
语法格式以下:
if(条件){
动做
}else{
动做
}
复制代码
示例:
$ awk 'BEGIN { num = 11; if (num % 2 == 0) { printf "%d 是偶数\n", num }else { printf "%d 是奇数\n", num } }'
复制代码
语法格式以下:
if(条件){
动做
}else if(条件){
动做
}else{
动做
}
复制代码
示例:
$ awk -F: '{ if($3<100) { print "系统用户", $1 } else if($3>100 && $3 <900) { print "普通用户", $1 } else { print "其它用户", $1 } }' /etc/passwd
复制代码
循环语句
while(条件){
循环体
}
复制代码
示例:
$ awk 'BEGIN {i=1; while(i<6){ print i; ++i } }'
复制代码
语法格式:
do{
循环体
}while(条件)
复制代码
示例:
awk 'BEGIN{i=1;do{print i;i++}while(i<6)}'
复制代码
语法格式:
for(变量;条件;表达式){
动做
}
复制代码
示例:
$ awk 'BEGIN{ sum=0; for(i=1;i<=100;i++){ sum+=i } print sum }'
复制代码
选项 | 含义 |
---|---|
-v | 参数传递 |
-V | 查看awk的版本号 |
-f | 指定脚本文件 |
-F | 指定分隔符 |
使用-v
引入外部变量
$ a=1
$ b="spring is framework"
$ awk -v c="$a" -v d="$b" 'BEGIN{print c , d}'
复制代码
把awk的执行动做写在脚本中,使用-f
指定脚本文件
$ vim print.awk
$ BEGIN{print c , d}
复制代码
执行脚本文件:print.awk
awk -v c="$a" -v d="$b" -f print.awk
复制代码
使用-F
指定分隔符
咱们以前可使用内置变量FS
来指定分隔符,好比:
$ awk 'BEGIN{FS=":"} {print $1}' /etc/passwd
复制代码
如今等价于:
$ awk -F ":" '{print $1}' /etc/passwd
# 或者
$ awk -F: '{print $1}' /etc/passwd
复制代码