Aha awk!

栏目: 服务器 · 发布时间: 7年前

内容简介:在开始学Linux的时候就听说过awk这个工具,但当时觉得,这个工具,好像平时用不太到啊,于是就没怎么学。不过最近写脚本的时候,在网上看到了一些awk的脚本,惊叹于awk的文本处理能力,我想,是时候拿起awk这个文本处理神器了。awk其实是先来个简单的程序,在终端输入下面代码:

在开始学 Linux 的时候就听说过awk这个工具,但当时觉得,这个工具,好像平时用不太到啊,于是就没怎么学。不过最近写脚本的时候,在网上看到了一些awk的脚本,惊叹于awk的文本处理能力,我想,是时候拿起awk这个文本处理神器了。

介绍

awk其实是 A ho, W inberger和 K ernighan三位大牛的首字母组合而成的。它既可以指awk这个可执行文件,也可以指awk这种语言,嗯,没错awk是一种语言!

Hello

先来个简单的程序,在终端输入下面代码:

$ awk -F: '$1 == "root" {print $0}' /etc/passwd

解释一下上面的这个命令, -F: 指的是分割符为 : ,后面字符串的意思是,如果被分隔符分隔的第一项为 root ,那就输出这一行到标准输出。而/etc/passwd是awk的处理的文本。

输出结果为:

root:x:0:0:root:/root:/bin/bash

语言

下面讲解一下awk的基本语法,其实awk的语法和C的语法有相似之处,如果不知道怎么用或者忘了,大可以用C的语法试试。

当然,如果忘了awk语法或者某个函数的用法,man吧,特别详细。

模式与行为

从上面这个命令中我们可以窥见awk语法的大致组成,它是有 模式行为 组成的。

对于每一行,如果前面的模式匹配了,那么条件后面紧跟的行为就会被执行。这个模式可以是类似于上面 $1 == "root" 的逻辑表达式,也可以是正则表达式。

上面的一行命令其实可以写成单独的脚本存储为a.awk:

BEGIN {
    FS=":"	#设定分隔符
}

$1 == "root" {
    print $0
}

然后使用命令 awk -f a.awk /etc/passwd ,执行效果是一样的。

模式

awk中的模式有如下几种(man awk):

BEGIN
END
BEGINFILE
ENDFILE
/regular expression/
relational expression	#也就是$1 == "root"这种,关系表达式
pattern && pattern
pattern || pattern
pattern ? pattern : pattern	#三目!awk强啊
(pattern)
! pattern
pattern1, pattern2		#这将会匹配到pattern1到pattern2之间的内容,是一种range pattern,如果忘了,man

正则模式示例,显示当前目录下,大小的单位为K的文件。

$ ls -lh | awk '/([0-9]+\.)?[0-9]+K/ { print $0}'

逻辑表达式示例,显示第3行的内容,NR的含义见下面的

$ echo -e "a \n b \n c" | awk 'NR == 3 {print $0}'

输出为c。

可以用正则来测试变量来匹配吗?

当然可以,见BEGIN&END下面的例子。

BEGIN&END

这两个是特殊的模式,跟在BEGIN后面的行为会在awk处理文件之前执行,一些初始化工作(比如设置FS)可以放到这里。

而END后面的行为则是在文件被处理完毕之后被执行,善后工作可以放到这里。

比如,下面的脚本统计目录下ctime为2019年的文件或目录数量:

#!/usr/bin/awk -f
# count2019.awk

BEGIN {
    CNT = 0;    # counter
}

$6~/2019(-[0-9]{2}){2}/ {	# 注意这里测试第6个字段是否匹配正则
    CNT ++;
}

END {
    printf "processing finised, 2019 files/dirs count: %d", CNT
}

运行:

$ ls --full-time | awk -f count2019.awk
processing finised, 2019 files/dirs count: 4⏎

行为

行为在模式的后面,需要用大括号括住。

行为中的语句可以用 ; 分割,也可以用换行符 \n 分隔。

变量

定义变量

awk中的变量直接用就行,和 python 一样,如果之前没有使用过这个变量,那么它的值为空。

数组

其实awk里面的数组就是python里面的字典,连字符串都能当索引使。

BEGIN {
    favorites["singer"] = "taylor swift"
    favorites["song"] = "500 miles"

    for (key in favorites) {
        print favorites[key]
    }
}

运行 awk -f array.awk 输出结果如下:

500 miles
taylor swift

内置变量

awk中有一些方便的内置变量,如下表(未全部列出):

变量名 作用
FILENAME 输入的文本文件的文件名,如果是标准输入,空。
$0 当前记录的内容
$1,$2,$3... 当前记录被分隔符分割成很多字段 $num 表示num项(1起始)
FS 分隔符,默认空格
RS 输入记录分隔符,默认为 \n
NF 当前记录的字段数
NR 已经读入的记录数
FNR 当前输入文件的记录数
OFS 输出字段分隔符,默认空格
ORS 输出记录分隔符,默认换行 \n
ARGC 命令行参数个数
ARGV 命令行参数数组

从上面的表中可以看到,其实一个文件是先被RS分隔成记录,然后这个记录再被FS分割成字段,所以准确地说awk其实是以记录为处理单元的,只不过这个RS默认是换行符。

ARGC&ARGV

运行一下下面的程序就知道他们的意思了:

#!/usr/bin/awk -f
# argc.awk

BEGIN{
    print ARGC
    for (i = 0 ; i < ARGC ; i++) {
        print ARGV[i]
    }
}
$ awk -f argc.awk /etc/passwd

输出结果:

2
awk
/etc/passwd

函数

函数调用的格式可以是 printf("%s %d", "hha", 3) 也可以是 printf "%s %d", "hah", 3

示例:

$ ls -l | awk '{printf "%15s %d\n",$9,$5}'
$ ls -l | awk '{printf ("%15s %d\n",$9,$5)}'

内置函数

下面列举一部分常用的内置函数

函数 功能
length(str) 字符串长度
index(str,sub) sub在str中的索引(1起始),如果无此子串,0
match(str, regex) 字面意思,0表示不匹配
split(str,arr,regex) 使用regex分割字符,把子字符串放入arr数组
printf(format, expr-list) 这个不解释
tolower(str)
toupper(str)
systime() 自1970-01-01,00:00:00至今的秒数,像不像time调用呢?
strftime([format [,timestamp[,utc-flag]]]) 返回一个格式化的时间字符串。示例见下面。
close(filename) 关闭一个文件(awk还能直接输出到文件!)
delete() 删除数组中的一个元素
exit(code) 退出,code是返回值
getline() 获取下一行,由于下一行被读了,所以此次记录处理完之后是下下行
next() 读取下一行,然后继续执行
system() 就是stdlib中的system那样的功能

systime&strftime

$ awk 'BEGIN {print strftime("%F",systime())}'

自定义函数

看个例子就会了:

#!/usr/bin/awk -f
# strlen.awk
function strlen(str) {
    return length(str)
}

BEGIN {
    message = "hello"
    printf "length of %s is %d", message, strlen(message)
}

控制语句

分支语句

只有一种,那就是 if...else... ,和C中一样的用法。

循环语句

四种,分别是 whiledo...whileforforeach (我善做主张起了个名字)

示例如下:

#!/usr/bin/awk -f
# loop.awk

BEGIN {
    for (i = 0 ; i < 10 ; i++){
        arr[i] = i*2;
    }

    for (idx in arr) {
        printf "arr[%d] = %d\n",idx,arr[idx]
    }

    i = 9 
    while (i >= 0) {
        arr[i] = 0;
        i--;
    }

    j = 0
    do{
        print arr[j]
        j++
    } while(j < 10)
}

运行 awk -f loop.awk 结果为:

arr[0] = 0
arr[1] = 2
arr[2] = 4
arr[3] = 6
arr[4] = 8
arr[5] = 10
arr[6] = 12
arr[7] = 14
arr[8] = 16
arr[9] = 18
0
0
0
0
0
0
0
0
0
0

输入输出

可以在awk脚本中直接输出到文件中,这需要用到awk中的重定向功能,和 shell 里面的重定向一样,使用的符号是 >,>> (当然没有输入重定向)

示例:

#!/usr/bin/awk
# redirect.awk
BEGIN {
    print "test" > "/tmp/awkoutput"
    close("/tmp/awkoutput") #一定要close,不然不会输出的,而且不关闭会资源泄漏
}

更多高级用法,就在实践中摸索吧!

参考资料:

AWK - Built-in Functions

《Linux就是这个范儿》(好书)


以上所述就是小编给大家介绍的《Aha awk!》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Convergence Culture

Convergence Culture

Henry Jenkins / NYU Press / 2006-08-01 / USD 30.00

"Convergence Culture" maps a new territory: where old and new media intersect, where grassroots and corporate media collide, where the power of the media producer, and the power of the consumer intera......一起来看看 《Convergence Culture》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具