Linux三大剑客之awk

一、概述

awk的名称来源于其最初设计者Alfred V. Aho, Peter J. Weinberger, and Brian W. Kernighan的姓氏。awk最原始的版本是1977年在AT&T贝尔实验室诞生的,awk经过改进生成的新的版本nawk,gawk,现在默认linux系统下日常使用的是gawk。我目前使用的操作系统为CentOS Linux release 7.2.1511,系统自带的awk版本为:GNU Awk 4.0.2。

二、awk的用途

  • 文本处理。
  • 格式化输出文本、报告。
  • 数字运算(在我之前的文章中,探讨过整数的算术运算,浮点运算可以使用awk)。
  • 执行字符串操作。

三、系统内几个有关awk命令的区别与联系

通过命令,我们可以看到:

[awk@GeekDevOps ~]$ ls -l /bin/*awk
lrwxrwxrwx. 1 root root      4 11月 20 10:41 /bin/awk -> gawk
-rwxr-xr-x. 1 root root 514136 6月  10 2014 /bin/dgawk
-rwxr-xr-x. 1 root root 428576 6月  10 2014 /bin/gawk
-rwxr-xr-x. 1 root root   3188 6月  10 2014 /bin/igawk
-rwxr-xr-x. 1 root root 428600 6月  10 2014 /bin/pgawk

与awk有关的命令竟然有这么多!在用whatis命令看一下这些都是些什么鬼:

[awk@GeekDevOps ~]$ whatis awk gawk dgawk pgawk igawk
awk (1)              - pattern scanning and processing language
gawk (1)             - pattern scanning and processing language
dgawk (1)            - pattern scanning and processing language
pgawk (1)            - pattern scanning and processing language
igawk (1)            - gawk with include files

不太确信以上结果,我又使用man、info挨个看了一下,结果awk、gawk、dgawk、pgawk都是gawk,在线手册一模一样。唯独igawk与前面四个不太一样,是包含文件的gawk,其实也是gawk。awk是gawk的一个软连接,大家都是gawk!

四、awk的使用

4.1 理解awk的工作原理

Linux三大剑客之awk_第1张图片
awk的工作原理

Read

awk从输入流(文件,管道或者标准输入)中读取一行,然后存储到内存中。

Execute

所有的AWK命令都依次在输入文本上执行。默认情况下,awk会对每一行执行命令,可以通过提供模式限制这种行为。

Repeat

处理过程不断重复,从首行开始直到到达文件结尾。

创建marks.txt文件备用:

[awk@GeekDevOps ~]$ echo "1)  Amit    Physics  80
> 2)  Rahul   Maths    90
> 3)  Shyam   Biology  87
> 4)  Kedar   English  85
> 5)  Hari    History  89">marks.txt
4.2 awk的结构

BEGIN 语句块

BEGIN语句块的语法:

BEGIN {awk-commands}

BEGIN语句块在程序开始的使用执行,只执行一次,在这里可以初始化变量。BEGIN是AWK的关键字,因此它必须为大写。注意,这个语句块是可选的。

BODY 语句块

BODY语句块的语法:

/pattern/ {awk-commands}

BODY语句块中的命令会对输入的每一行执行,也可以通过提供模式来控制这种行为。注意,BODY语句块没有关键字。

END 语句块

END语句块的语法:

END {awk-commands}

END语句块在程序的最后执行,END是AWK的关键字,因此必须为大写,它也是可选的。

结合以上2点,举个例子来理解一下:

[awk@GeekDevOps ~]$ awk BEGIN'{printf "Sr NO\tName\tSub\tMarks\n"} {print} END{print "Done!"}' marks.txt
Sr NO   Name    Sub     Marks
1)  Amit    Physics  80
2)  Rahul   Maths    90
3)  Shyam   Biology  87
4)  Kedar   English  85
5)  Hari    History  89
Done!
[awk@GeekDevOps ~]$ awk BEGIN'{printf "Sr NO\tName\tSub\tMarks\n"} END{print "Done!"}' marks.txt
Sr NO   Name    Sub     Marks
Done!

在本例中,BEGIN块被执行一次,给文本内容增加了一行标题。body块从文本第一行扫描直至文件末尾。END块也仅仅执行了一次。

4.3 使用awk

通常情况下,awk命令较简短时我们直接按照以下方式执行awk命令:

awk 'program' input-file1 input-file2 …

如果awk内容较多的话,我们以以下格式来执行awk命令(此处program-file为awk脚本内容):

awk -f program-file input-file1 input-file2 …

awk是一门解释型的语言,所以也可以像执行bash shell一样执行awk脚本:

#!/bin/awk -f
BEGIN{print "My name is Ivan Du!"}
[awk@GeekDevOps ~]$ chmod u+x GeekDevOps.awk
[awk@GeekDevOps ~]$ ./GeekDevOps.awk
My name is Ivan Du!
4.4 打印某列或某字段
[awk@GeekDevOps ~]$ cat best.txt
www     CC      ICBC
[awk@GeekDevOps ~]$ awk '{print $1}' best.txt
www
[awk@GeekDevOps ~]$ awk '{print $3}' best.txt
ICBC
[awk@GeekDevOps ~]$ awk -F "\t" '{print $3}' best.txt
ICBC

默认情况下,awk以空格、制表符等符号为分隔符。从每一行的第一个字符串开始扫描,第一个字符串为n,如果首行是空格之类的字符那也算。

4.5 打印匹配模式的列

当模式匹配成功时,默认情况下awk会打印该行,但是也可以让它只打印指定的字段。例如,下面的例子中,只会打印出匹配模式的第三和第四个字段。

[awk@GeekDevOps ~]$ awk '/a/ {print $3 " " $4}' marks.txt
Maths 90
Biology 87
English 85
History 89

匹配指定文件中带u的行,并打印第四列与第三列,中间以一个横向制表符隔开。

[awk@GeekDevOps ~]$ awk '/u/ {print $4"\t"$3}' marks.txt
90      Maths
4.6 统计文本总行数
[awk@GeekDevOps ~]$ awk 'BEGIN{ct=0} {++ct} END{print "Count:",ct}' marks.txt
Count: 5

此行命令中,BEGIN部分其实是可以省略的。

4.7 打印匹配模式的总行数
[awk@GeekDevOps ~]$ awk '/a/ {++ct} END{print "Count:",ct}' marks.txt
Count: 4

此处的BEGIN部分不能出现,否则报错。

4.8 打印超过指定长度的行
[awk@GeekDevOps ~]$ awk 'length($0) > 22' marks.txt
1)  Amit    Physics  80
2)  Rahul   Maths    90
3)  Shyam   Biology  87
4)  Kedar   English  85
5)  Hari    History  89

mark.txt文件是通过空格来控制对齐的,每一行加上空格一共是22个字符。

4.9 ARGC命令行参数个数
[awk@GeekDevOps ~]$ awk 'BEGIN{print "Arguments=" ARGC}' One Two Three Four
Arguments=5
4.10 ARGV命令行参数数组
[awk@GeekDevOps ~]$ awk 'BEGIN{for (i=0 ;i
4.11 ENVIRON环境变量
[awk@GeekDevOps ~]$ awk 'BEGIN{print ENVIRON["PATH"]}'
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/awk/.local/bin:/home/awk/bin
4.12 FILENAME当前文件名
[awk@GeekDevOps ~]$ awk 'END{print FILENAME}' marks.txt 
marks.txt

注意:此处使用的是END,如果省略则会打印出5个文件名称,这个文件一5行。

4.13 常用的awk内置变量
变量名 属性
$0 当前记录
n 当前记录的第N个字段
FS 读入字段的分隔符,默认为空格
RS 读入记录分隔符,默认为换行符
NF 列,当前记录中的字段个数
NR 行,已经读出的行数,也就是行号
OFS 输出字段分隔符,默认为空格
ORS 输出记录分隔符,默认为换行符
[awk@GeekDevOps ~]$ awk -F " " '{print $4}' marks.txt 
80
90
87
85
89
[awk@GeekDevOps ~]$ awk '{print $3}' marks.txt 
Physics
Maths
Biology
English
History
[awk@GeekDevOps ~]$ awk 'BEGIN{FS=" "}{print $3}' marks.txt 
Physics
Maths
Biology
English
History

以上三种写法都是一样的效果。

[awk@GeekDevOps ~]$ ls -al | awk 'BEGIN{size=0}{size+=$5}END{print size/1024/1024 "MB"}'
128.017MB
[awk@GeekDevOps ~]$ ls -al|awk 'NR>1{size+=$5} END{print size/1024/1024 "MB"}'
128.017MB
[awk@GeekDevOps ~]$ ls -al|awk 'NR==1{print $2/1024 "MB"}'
128.035MB

以上两个代码片中,都是统计当前目录下所有的文件(包括隐藏文件)所占磁盘空间的大小,非常有用。

[awk@GeekDevOps ~]$ awk 'BEGIN{print "OFS=" OFS}' marks.txt
OFS=
4.14 awk中的算术运算

在awk中,支持像C语言中一样的算术运算。在前面的文章中介绍过,Linux系统中无法对非整型数字直接进行算术运算,要对浮点型的数据就行算术运算我们可以使用awk来实现。

[awk@GeekDevOps ~]$ awk 'BEGIN{A=2.8;B=7;print A/B}'
0.4

在这一部分中,awk中的增减运算符、赋值运算符、关系操作符、逻辑运算符、三元操作符等均与C语言类似,不赘述。

4.15 awk中的一元操作符
[awk@GeekDevOps ~]$ awk 'BEGIN{A=2.8;A=+A;print A}'
2.8
4.16 awk中的指数操作符
[awk@GeekDevOps ~]$ awk 'BEGIN{A=2.8;A=A^3;print A}'
21.952
[awk@GeekDevOps ~]$ awk 'BEGIN{A=2.8;A=A**3;print A}'
21.952
4.17 awk中的字符串连接操作符
[awk@GeekDevOps ~]$ awk 'BEGIN{str1="Hello";str2=",GeekDevOps";str3=str1 str2;print str3}'
Hello,GeekDevOps
4.18 awk中的数组

数组的定义与C语言有类似的地方,也有差别,使用的时候需要注意一下,有的资料上说awk中不支持多维数组,通过awk的GUN的文档,我们可以看到,awk也是支持多维数组的。删除数组元素使用delete语句:

[awk@GeekDevOps ~]$ awk 'BEGIN{array[0][0]=2;array[0][1]=3;array[1][0]=4;array[1][1]=8;for(i=0;i<2;i++){for(j=0;j<2;j++){printf "array[%d][%d]=%d\n",i,j, array[i][j]}}delete array[0][0];print array[0][0] "\t" array[0][1]}'
array[0][0]=2
array[0][1]=3
array[1][0]=4
array[1][1]=8
        3
4.19 awk中的控制结构

在awk中,也支持类似C语言一样的程序结构,支持if-else、while、do-while、for、switch、break、continue、nex、nextfile、exit。相信很多小伙伴都学习过C语言,与C语言一样的就不在赘述。

[awk@GeekDevOps ~]$ awk 'NF != 4 {
    printf("%s:%d: skipped: NF != 4\n", FILENAME, FNR) > "/dev/stderr"
    next
}'

-:1: skipped: NF != 4

-:2: skipped: NF != 4
...
[awk@GeekDevOps ~]$ awk 'BEGIN {
    if (("date" | getline date_now) <= 0) {
        print "Can not get system date" > "/dev/stderr"
        exit 1
    }
    print "Current date is", date_now
    close("date")
}'
Current date is 2018年 02月 28日 星期三 23:05:16 CST
4.20 awk中的函数

在awk中,支持内建函数与用户自定义函数。

常用数学计算函数

atan2(y, x)

Return the arctangent of y / x in radians. You can use ‘pi = atan2(0, -1)’ to retrieve the value of pi.

cos(x)

Return the cosine of x, with x in radians.

exp(x)

Return the exponential of x (e ^ x) or report an error if x is out of range. The range of values x can have depends on your machine’s floating-point representation.

int(x)

Return the nearest integer to x, located between x and zero and truncated toward zero. For example, int(3) is 3, int(3.9) is 3, int(-3.9) is -3, and int(-3) is -3 as well.

log(x)

Return the natural logarithm of x, if x is positive; otherwise, return NaN (“not a number”) on IEEE 754 systems. Additionally, gawk prints a warning message when x is negative.

rand()

Return a random number. The values of rand() are uniformly distributed between zero and one. The value could be zero but is never one.

sin(x)

Return the sine of x, with x in radians.

sqrt(x)

Return the positive square root of x. gawk prints a warning message if x is negative. Thus, sqrt(4) is 2.

srand([x])

Set the starting point, or seed, for generating random numbers to the value x. 

字符串函数

asort(arr [, d [, how] ])
asorti(arr [, d [, how] ])
gsub(regex, sub, string)
index(str, sub)
length(str)
match(str, regex)
split(str, arr, regex)
sprintf(format, expr-list)
strtonum(str)
sub(regex, sub, string)
substr(str, start, l)
tolower(str)
toupper(str)

时间函数

systime
mktime(datespec)
strftime([format [, timestamp[, utc-flag]]])

字节操作函数

and
compl
lshift
rshift
or
xor

自定义函数类似于C语言,参阅GUN网站function部分。

4.21 awk中的正则表达式
[awk@GeekDevOps ~]$ echo -e "My name is IVAN DU.\nMy blog is GeekDevOps.\nWelcome to my blog."|awk '/My+/'
My name is IVAN DU.
My blog is GeekDevOps.
[awk@GeekDevOps ~]$ echo -e "My name is IVAN DU.\nMy blog is GeekDevOps.\nWelcome to my blog."|awk '/^W/'
Welcome to my blog.

五、参考资料

https://www.tutorialspoint.com/awk/index.htm
https://www.gnu.org/software/gawk/manual/gawk.html

你可能感兴趣的:(Linux三大剑客之awk)