Aha awk!

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

内容简介:在开始学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!》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

Algorithms to Live By

Algorithms to Live By

Brian Christian、Tom Griffiths / Henry Holt and Co. / 2016-4-19 / USD 30.00

A fascinating exploration of how insights from computer algorithms can be applied to our everyday lives, helping to solve common decision-making problems and illuminate the workings of the human mind ......一起来看看 《Algorithms to Live By》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

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

正则表达式在线测试