linux三剑客之awk命令详解
awk 是一个处理文本的编程语言工具,能用简短的程序处理标准输入或文件、数据排序、计算以及生成报表等等。awk工具主要用于格式化输出,可以用类似于处理表格的方式处理文本,也就是说可以将文本处理成表格的格式,包含行和列,然后可以执行打印某些行、打印某些列、删除某些行、删除某些列等操作。
awk与grep、sed一样均支持从管道符接受标准输入和从命令行接受参数,awk同样也支持正则表达式。
awk 处理的工作方式与数据库类似,支持对记录(行)和字段(列)处理,这也是 grep 和 sed 不能实现的。
在 awk 中,缺省的情况下将文本文件中的一行视为一个记录,逐行放到内存中处理,而将一行中的某一部分作为记录中的一个字段。用 1,2,3…数字的方式顺序的表示行(记录)中的不同字段。用$后跟数字,引用对应的字段,以逗号分隔,0 表示整个行。
基本的命令语法:awk option ‘pattern {action}’ file
功能参数+模式+动作
是不是这个语法看起来好眼熟,确实是,跟sed的语法差不多,只不过awk可以将行分割成多个字段,然后选择性的打印这些字段,而sed则只能对整行进行处理。
栗子:awk -F ':' '/root/{print $3}' /etc/passwd
在passwd配置文件中找到包含root的行并用冒号分隔成多个字段,然后打印第三个字段
[root@linuxforliuhj ~]# awk -F ':' '/root/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[root@linuxforliuhj ~]# awk -F ':' '/root/{print $1}' /etc/passwd
root
operator
-F:功能参数,指定每一行的分隔符为冒号,默认为空白字符
/root/:模式,正则表达式,表示只处理包含root的行
{print $3}:动作,打印第3个字段
1、功能参数
功能参数 | 解释 |
---|---|
-F | 指定分隔符 |
-v | 变量赋值 |
【1】指定分隔符
有如下文件
[root@linuxforliuhj test]# cat a.txt
avahi:x:70:70:Avahi mDNS/DNS-SD Stack:/var/run/avahi-daemon:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
hadoop101:x:1000:1000:刘浩君的linux:/home/hadoop101:/bin/bash
hadoop102:x:1001:1002::/home/hadoop102:/bin/bash
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
以冒号分隔每一行并打印每一行的第一个字段:
cat a.txt | awk -F ':' '{print $1}'
[root@linuxforliuhj test]# cat a.txt | awk -F ':' '{print $1}'
avahi
postfix
tcpdump
hadoop101
hadoop102
mysql
此时我们只指定了冒号为分隔符,如果我想指定两个字符为分隔符呢,则需要使用[ ]将分隔符括起来
cat a.txt | awk -F '[:#]' '{print $1}'
这样在处理每一行的时候只要遇到冒号或者#号就分隔
【2】变量赋值
以冒号分隔每一行并打印第三列的值乘以3
cat a.txt | awk -F ':' -v h=3 '{print h*$3}'
[root@linuxforliuhj test]# cat a.txt | awk -F ':' '{print $3}'
70
89
72
1000
1001
27
[root@linuxforliuhj test]# cat a.txt | awk -F ':' -v h=3 '{print h*$3}'
210
267
216
3000
3003
81
同时也可以传入变量
cat a.txt | awk -F ':' -v a=$a '{print a*$3}'
[root@linuxforliuhj test]# a=`cat a.txt | wc -l`
[root@linuxforliuhj test]# echo $a
6
[root@linuxforliuhj test]# cat a.txt | awk -F ':' -v a=$a '{print a*$3}'
420
534
432
6000
6006
162
主要的功能参数就只有这两个,指定分隔符和变量赋值,下面继续看模式,即对哪些行进行处理,相当于grep,但是比grep更高级一点。
2、模式
在开始讲述模式之前,我们先说一下BEGIN和END模式
BEGIN 模式是在处理文件之前执行该操作,常用于修改内置变量、变量赋值和打印输出的页眉或标题。
awk -F ':' 'BEGIN{print "用户名\t\t\t\t权限"}/root/{printf "%-30s%-30s\n", $1,$7}'
[root@linuxforliuhj test]# awk -F ':' 'BEGIN{print "用户名\t\t\t\t权限"}/root/{printf "%-30s%-30s\n", $1,$7}' /etc/passwd
用户名 权限
root /bin/bash
operator /sbin/nologin
END则是在处理完文本之后打印的内容
awk -F ':' 'BEGIN{print "用户名\t\t\t\t权限"}/root/{printf "%-30s%-30s\n", $1,$7}END{print "--------------------\n打印结束!"}' /etc/passwd
[root@linuxforliuhj test]# awk -F ':' 'BEGIN{print "用户名\t\t\t\t权限"}/root/{printf "%-30s%-30s\n", $1,$7}END{print "--------------------\n打印结束!"}' /etc/passwd
用户名 权限
root /bin/bash
operator /sbin/nologin
--------------------
打印结束!
其次我们再说一下常用的awk内置变量,内置变量就相当于在awk处理文本的过程中生成的变量,例如NR表示每一行的行数,NF则表示分割后的字段数等等,如果想要改变内置变量,例如默认输出时每个字段之间的分隔符是Tap,可以在BEGIN中改变输出分隔符。
[root@linuxforliuhj test]# awk -F ':' '/hadoop/{print $1,$3}' /etc/passwd
hadoop101 1000
hadoop102 1001
[root@linuxforliuhj test]# awk -F ':' 'BEGIN{OFS="---"}/hadoop/{print $1,$3}' /etc/passwd
hadoop101---1000
hadoop102---1001
awk内置变量:
变量名 | 解释 |
---|---|
FS | 分隔符,默认空格 |
OFS | 输出分隔符,默认空格 |
RS | 每一行的分隔符,默认\n |
ORS | 输出时每一行的分隔符,默认\n |
NF | 分割以后的字段数 |
NR | 行号 |
FILENAME | 当前输入文件名 |
ARGV | 包含命令行参数的数组 |
ARGC | 命令行参数的个数 |
现在开始介绍模式,模式就是我们要选择哪些行进行处理,是选择包含root的行还是前5行,我们自己可以设定
模式 | 解释 |
---|---|
/regexp/ | 匹配满足正则表达式的行 |
+ - * / % ^ | 加减乘除取余指数 |
= | 变量赋值 |
|| && | 或者 并且 |
~ !~ | 某个字段满足某个正则或者不满足某个正则 |
== >= <= != > < | 大于等于小于不等于 |
【1】匹配正则
awk -F ':' '/hadoop/{print $1,$3,$NF}' /etc/passwd
匹配包含hadoop的行并且以冒号分隔匹配到的行然后打印第1、3和最后一个字段
[root@linuxforliuhj test]# awk -F ':' '/hadoop/{print $1,$3,$NF}' /etc/passwd
hadoop101 1000 /bin/bash
hadoop102 1001 /bin/bash
[root@linuxforliuhj test]# awk -F ':' '/hadoop/{print $1,$3,NF}' /etc/passwd
hadoop101 1000 7
hadoop102 1001 7
[root@linuxforliuhj test]# awk -F ':' '/hadoop/{print $1,$3,$7}' /etc/passwd
hadoop101 1000 /bin/bash
hadoop102 1001 /bin/bash
NF属于内置变量,代表分割后的字段数,直接打印NF的值为7,则打印$NF的值即为打印$7的值。
【2】加减乘除简单计算
head -n 20 /etc/passwd | cat -n | awk -F ':' 'NR%2==0{print $1,$3,$NF}'
打印偶数行的第1、3和最后一个字段
[root@linuxforliuhj test]# head -n 20 /etc/passwd | cat -n | awk -F ':' 'NR%2==0{print $1,$3,$NF}'
2 bin 1 /sbin/nologin
4 adm 3 /sbin/nologin
6 sync 5 /bin/sync
8 halt 7 /sbin/halt
10 operator 11 /sbin/nologin
12 ftp 14 /sbin/nologin
14 systemd-network 192 /sbin/nologin
16 polkitd 999 /sbin/nologin
18 colord 997 /sbin/nologin
20 saned 996 /sbin/nologin
内置变量NR为每一行的行数,当行数为偶数时,则打印偶数行的第1、3、7个字段,因为passwd位置文件条数太多了,所以为了方便只取前20行做测试。
【3】变量赋值
awk -F ':' 'BEGIN{OFS="---"}/hadoop/{print $1,$3}' /etc/passwd
变量赋值一般使用在BEGIN模式中,也就是说在awk处理文本之前,先执行BEGIN中的赋值操作,然后再处理文本。
【4】或者和并且
cat -n /etc/passwd | awk 'BEGIN{FS=":"}NR==1||NR==5{print $1,$3,$NF}'
打印第一行和第五行的某些字段则需要使用NR指定行数,行数为1或者行数为5,所以要用||
隔开,同时这里在BEGIN中修改FS变量的值跟使用-F参数是一样的。
[root@linuxforliuhj test]# cat -n /etc/passwd | awk 'BEGIN{FS=":"}NR==1||NR==5{print $1,$3,$NF}'
1 root 0 /bin/bash
5 lp 4 /sbin/nologin
【5】某个字段匹配某个正则
awk -F ":" '$1~/hadoop/{print $1,$3,$7}' /etc/passwd
打印以冒号分割每一行并且第一个字段满足正则/hadoop/
的行的第1、3、7列,使用~符号来使某个字段匹配某个正则,这也是awk比grep更高级的地方,它可以指定某个字段满足某个正则,grep则只能过滤整行。
[root@linuxforliuhj test]# awk -F ":" '$1~/hadoop/{print $1,$3,$7}' /etc/passwd
hadoop101 1000 /bin/bash
hadoop102 1001 /bin/bash
[root@linuxforliuhj test]# awk -F ":" '$2~/hadoop/{print $1,$3,$7}' /etc/passwd
[root@linuxforliuhj test]#
【6】大于小于
awk -F ":" '$1~/oo/&&NR>30{print NR,$1,$3,$7}' /etc/passwd
以冒号分割每一行并且第一列字段包含oo并且行数大于30,打印行号和第1、3、7列。
[root@linuxforliuhj test]# awk -F ":" '$1~/oo/{print NR,$1,$3,$7}' /etc/passwd
1 root 0 /bin/bash
24 setroubleshoot 993 /sbin/nologin
44 hadoop101 1000 /bin/bash
45 hadoop102 1001 /bin/bash
[root@linuxforliuhj test]# awk -F ":" '$1~/oo/&&NR>30{print NR,$1,$3,$7}' /etc/passwd
44 hadoop101 1000 /bin/bash
45 hadoop102 1001 /bin/bash
【7】栗子
以:分割文件并且打印第一个字段不以r开头的行:
awk 'BEGIN{FS=":"}$1!~/^r/{print $0}' /etc/passwd
以:分割文件并且打印第一个字段以r开头的行
awk 'BEGIN{FS=":"}$1~/^r/{print $0}' /etc/passwd
以:分割每一行并且打印第9行和第10行
awk 'BEGIN{FS=":"}NR==9||NR==10{print $0}' /etc/passwd
以:分割每一行并且打印行数的平方等于9的行
awk 'BEGIN{FS=":"}NR^2==9{print $0}' /etc/passwd
以:分割每一行并且打印偶数行
awk 'BEGIN{FS=":"}NR%2==0{print $0}' /etc/passwd
以:分割每一行并且打印第5行
awk 'BEGIN{FS=":"}NR==5{print $0}' /etc/passwd
以:分割每一行并且打印第5行的第三个字段
awk -F ':' 'NR==5{print $3}' /etc/passwd
以:分割每一行并且打印第一个字段为"root"的字段
awk -F ':' '$1=="root"{print $0}' /etc/passwd
以:分割每一行并且打印第2-5行
awk -F ':' 'NR>=2&&NR<=5{print $0}' /etc/passwd
以:分割字段并且打印第一个字段正则匹配hadoop101的行
awk -F ':' '$1 ~ /hadoop101/{print $0}' /etc/passwd