这是我参与8月更文挑战的第12天,活动详情查看:8月更文挑战
awk
AWK是一种处理文本文件的语言。它将文件作为记录序列处理。
在一般情况下,文件内容的每行都是一个记录。每行内容都会被分割成一系列的域,因此,我们可以认为一行的第一个词为第一个域,第二个词为第二个,以此类推。
AWK程序是由一些处理特定模式的语句块构成的。AWK一次可以读取一个输入行。对每个输入行,AWK解释器会判断它是否符合程序中出现的各个模式,并执行符合的模式所对应的动作。
——阿尔佛雷德·艾侯,The A-Z of Programming Languages: AWK
awk命令的通用语法格式
awk '{commands}'
复制代码
commands
为一个或多个命令- 常用选项:
-f
,表明awk命令将从该标志之后的文件中读取指令而不是从命令行读取;-Fc
,这个标志表明字段之间的分隔符是c而不是默认的空白字符(如制表键、一个或多个空格符)。
常用点:
print
:将一行接一行地打印出文件中的所有数据行。
可用 print str1 str2 int1,int2...
也可 print(A,B,C,...)
$ who | awk '{ print }' # 这个就等于直接使用「who」
复制代码
如果要打印 \n
, \t
等字符:
$ ls -lF /boot | awk '{ print $5 "\t" $9}' | sort -rn | head -3
复制代码
(这行命令的作用是:列出 /boot
目录中每一个文件的文件名和大小,文件的大小在前,而文件名随后,文件大小和文件名由制表键隔开,按大小反序排列(从大到小)把其中最大的3个文件输出出来。利用到了下文的 $n
用法,后面会详细介绍)
awk 中也可以用 c 风格的 printf():
printf("Total: %s\n",totalsize) # 注意'\n'
复制代码
$n
:字段变量
在文件和Linux命令的结果显示中,每行信息被指定的分隔符分隔成若干个字段,每个字段都被赋予一个唯一的标识符。
如,字段1的标识符是 $1
,字段2的标识符是 $2
等。特殊地,有变量 $0
表示整行 ($0=re('^\*$')
)。
awk 中,会大量使用到这种字段标识,下面是一些例子:
-
列出who命令显示结果中每行的第1个字段,即目前登录Linux系统的用户名
$ who | awk '{ print $1 }' 复制代码
-
在上一个命令的基础上加入一些解释性的话:
$ who | awk '{ print "User " $1 " is on terminal line " $2}' 复制代码
-
emp.data 文件中的第2个字段为员工姓,第4个字段为工资。在员工姓前加上Employee,在员工的姓和工资之间加上 has salary 字符串,再输出:
$ awk '{ print "Employee " $2 " has salary " $4}' emp.data 复制代码
-
在
/etc/passwd
中所有的字段都是以:
分隔的。现在要获取某些用户(以 foo 与 bar 为例)登录时使用的shell,可以使用如下步骤:- 1). 可以考虑用 grep 从
/etc/passwd
文件中抽取包含目标用户foo
或bar
的数据行; - 2). 用 awk 命令把冒号看成字段的分隔符并将列出第1个(用户名)和第7个字段(登录时的shell);
- 3). 为了方便阅读,在显示结果中加入一些描述信息以帮助阅读和理解;
具体实现如下:
$ egrep 'foo|bar' /etc/passwd | awk -F: '{ print $1" has " $7 " as loggin shell." }' 复制代码
- 1). 可以考虑用 grep 从
-
获取系统上各个shell分别被几个用户默认使用:
$ awk -F: '{ print $7 }' /etc/passwd | sort | uniq -c 复制代码
-
获取哪些用户在登录时使用的 shell 是存放在
/bin
目录中以及这个 shell 的名字;同时对于登陆使用/bin/sync
的 sync 用户,不让它出现在显示的结果;最后进行排序。$ grep /bin/ /etc/passwd | awk -F: '{ print $1" " $7 }' | sed '/sync/d' | sort 复制代码
-
在每个用户记录的最前面显示这个用户登录Linux系统所使用的计算机(who 命令显示结果中的最后一个字段是用户登录 Linux 系统所使用的计算机的 IP 地址,如果为空表示是本机登录)
$ who | awk '{ print $6": "$0}' 复制代码
NF
、NR
变量
NF
与 $NF
-
NF
(没有$
符号的NF变量):表示一行记录中有多少个字段。 -
$NF
(带有$
符号的NF变量): 一行记录中的最后一个字段(即,第NF个字段,也就是最后一个字段)
e.g.
-
列出who命令显示结果中每一行的字段数(列数):
$ who | awk '{ print NF }' 复制代码
-
列出who命令显示结果中每一行的最后一个字段:
$ who | awk '{ print $NF }' 复制代码
-
一个复杂的例子:
$ egrep 'bin|sbin' /etc/passwd | awk -F: '{ print $NF }' | sort | uniq -c | sort -n 复制代码
egrep
:从/etc/passwd文件中抽取包含bin或sbin的数据行awk
:把冒号看成字段的分隔符,列出每一行的最后一个字段sort
:将那些字段进行排序uniq -c
:合并相同行,并在每行前面冠以该行出现次数sort -n
:按次数的大小进行排序。
NR
变量 NR
用来追踪所显示的数据行的数目,即显示数据行的编号。
例如:
$ ls -l ~/wolf | awk '{ print NR": "$0}' # 用NR给每行一个编号
1: total 16
2: drwxrwxr-x 2 dog dog 4096 Jan 25 2009 boywolf
3: -rw-rw-r-- 1 dog dog 84 Dec 22 19:07 delete_disable\
......
复制代码
awk中计算
awk 可以使用 C 语言的运算符、if条件、for循环。但变量使用前不用声明。
e.g.
获取 /boot
目录中所有文件大小的总和(将得到累加过程中每一步的 totalsize 值):
$ ls -lF /boot | awk '{ totalsize += $5; print totalsize }'
复制代码
我们只需要最终结果,不输出中间值,可以用 tail 取最后一个:
$ ls -lF /boot | awk '{ totalsize += $5; print totalsize }' | tail -1
复制代码
但除了使用 tail 命令之外,一种更好的方法是在 awk 命令中使用 END
关键字。
END
关键字
在最后一步执行 END
后的 {statements}
:
$ ls -lF /boot | awk '{ totalsize += $5} END { print totalsize }'
复制代码
if 条件、for 循环
使用 if 的例子:
$ awk '{ if (length($4) == 3 ) print $0 }' emp.data | wc -l
复制代码
使用 for 循环和 if 类似,直接写 for(i=0; i < 10; i++)
这样的即可。
将 commands
表达式放入文件
前面看到了,awk 的 commands 可以写的很复杂,基本上是一个程序了,所以我们可以把它放到一个文件中:
用你喜欢的编辑器把 commands 写完,保存在例如 script1
文件中。然后就可以用 awk 的 -f
来从文件中读取 commands:
$ ls -lF /boot | awk -f script1
复制代码