7. awk
awk 是一门解释型的编程语言,用于文本处理,它的名字来源于三位作者的姓氏首字母。
Note
查看 awk 帮助文档: man awk
或 awk --help
。
7.1. 命令模式
awk 'BEGIN {awk-commands} pattern {awk-commands} END {awk-commands}' fileName
awk-commands 代码块必须包含在花括号
{}
中。匹配模式 pattern 可选 ,用于筛选符合条件的记录(行),可以使用正则表达式。
BEGIN 语句块
BEGIN {awk-commands}
可选 ,只执行一次,在这里可以初始化变量。BODY 语句块
pattern {awk-commands}
,这里的命令会对输入的每一行执行。如果没有
fileName
或其他输入流且存在 BODY 语句块,BODY 语句块会进入死循环。代码语句表达式以分号结束,也可以用换行符结束。
如果没有
{awk-commands}
,默认的动作是打印记录(行)。
END 语句块
END {awk-commands}
可选 ,在处理完所有行之后执行。
7.2. 常用的内建变量
变量 |
描述 |
---|---|
$0 |
完整的输入记录(当前行的内容) |
$n |
当前记录(当前行)的第 n 个字段,字段间由 |
ARGC |
命令行参数数目 |
ARGV |
命令行参数数组 |
ENVIRON |
环境变量 |
ERRNO |
最后一个系统错误的描述 |
FILENAME |
当前文件名 |
FS |
字段分隔符(默认是空格、制表符);通过 |
IGNORECASE |
进行忽略大小写的匹配 |
NF |
一条记录(一行)的字段的数目 |
NR |
已经读出的记录数,即行号,从 1 开始 |
FNR |
和 NR 类似;如果存在多个输入文件,FNR 是当前文件的行号 |
OFS |
输出字段分隔符(默认是一个空格);通过 |
ORS |
输出行分隔符(默认是一个换行符);通过 |
RLENGTH |
由 match 函数所匹配的字符串的长度 |
RS |
记录分隔符(默认是一个换行符) |
RSTART |
由 match 函数所匹配的字符串的第一个位置 |
ARGIND |
循环处理数据时,当前被处理的 ARGV 的索引 |
PROCINFO |
包含进程信息的关联数组,例如UID、进程ID等 |
1% awk 'BEGIN {print ENVIRON["USER"]}'
2fong
3% awk 'END {print FILENAME}' test.txt
4test.txt
5% awk 'BEGIN {
6 for (i = 0; i < ARGC; ++i) {
7 printf "ARGV[%d] = %s\n", i, ARGV[i]
8 }
9}' first second
10ARGV[0] = awk
11ARGV[1] = first
12ARGV[2] = second
1% echo "a b c\n1\t2\t3" > test.txt
2% cat test.txt
3a b c
41 2 3
5% awk '{print $0 $1 $2}' test.txt
6a b cab
71 2 312
8% awk '{print $0,$1,$2}' test.txt
9a b c a b
101 2 3 1 2
11% awk 'BEGIN{OFS="#"; ORS="***"} {print "LINE-"NR,$0,$1,$2}' test.txt
12LINE-1#a b c#a#b***LINE-2#1 2 3#1#2***
13% awk 'BEGIN{OFS="#"} {$1=$1; print "LINE-"NR,$0,$1}' test.txt
14LINE-1#a#b#c#a
15LINE-2#1#2#3#1
打印语句中需要使用 ,
作为分隔符,如果是空格则 OFS
不会生效。如果想对原始输入( $0
)也使用输出分隔符,需要先对输入字段进行操作(如 $1=$1
)。
Note
awk 命令中的字符串使用双引号。
7.3. 基础语法
流程控制
#-------- 伪代码 1 ---------
if (condition)
代码逻辑...
else if(condition)
代码逻辑...
else
代码逻辑...
#-------- 伪代码 2 ---------
for (初始化; condition; 后续逻辑){
代码逻辑...
}
#-------- 伪代码 3 ---------
while (condition){
代码逻辑...
}
#-------- 伪代码 4 ---------
do{
代码逻辑...
}while (condition)
常用运算符
符号 |
说明 |
示例 |
---|---|---|
|
指数操作符 |
|
|
一元操作符 |
|
|
三元操作符 |
|
|
逻辑操作符 |
|
|
关系运算符 |
|
% awk 'BEGIN{a=3; b=2; a > b ? c=a: c=b; print c}'
3
数组
awk 支持普通数组和关联数组,也就是说,不仅可以使用数字索引(从 1 开始)的数组,还可以使用字符串作为索引。
1% awk 'BEGIN {arr["a"] = 1; arr["b"] = 2; for (i in arr) printf "arr[%s] = %d\n", i, arr[i]}'
2arr[a] = 1
3arr[b] = 2
删除数组元素使用 delete 语句如 delete arr[0]
;获取数组长度 length(arr)
。
字符串操作
length(str)
:获取 str 长度。match(str, regex)
: str 是否匹配 regex 模式,返回布尔值,匹配的位置保存在RSTART
和RLENGTH
。split(str, arr, fs)
:使用 fs(缺省为FS
) 分隔字符串 str,结果保存在数组 arr 中,返回数组 arr 的长度。substr(str, start, l)
:返回子字符串 str[start:start+l]。tolower(str)
:转小写。toupper(str)
:转大写。
1% awk 'BEGIN { str1 = "hello"; str2 = "world"; str3 = str1" "str2; print str3, length(str3); if(match(str3, "h.*w")) print RSTART, RLENGTH}'
2hello world 11
31 7
正则表达式
awk 支持正则表达式,需要放在斜杠中: /regexp/
。另外,awk 还有两个匹配符: ~
和 !~
分别代表匹配和不匹配,搭配正则表达式使用。
1% echo "tom man 20\njerry man 18\nalice woman 25" > test.txt
2% cat test.txt
3tom man 20
4jerry man 18
5alice woman 25
6# 第 2 个字段包含 woman 的行
7% awk '$2 ~ /woman/' test.txt
8alice woman 25
9# 第 3 个字段不以 1 开头的行
10% awk '$3 !~ /^1[0-9]*/' test.txt
11tom man 20
12alice woman 25
13# 包含 man 的行
14% awk '/man/' test.txt
15tom man 20
16jerry man 18
17alice woman 25
7.4. 运行文件脚本
脚本以 .awk
为后缀,执行: awk -f commands.awk test.txt
。
1% cat a.awk
2BEGIN{
3 cnt = 0;
4}
5
6{cnt++; print NR, $0}
7
8END{
9 print FILENAME, cnt" lines"
10}
11% awk -f a.awk test.txt
121 tom man 20
132 jerry man 18
143 alice woman 25
15test.txt 3 lines
7.5. 参考资料
技能篇:awk教程-linux命令
awk