awk是一个强大的文本处理工具。
awk把文件逐行的读入,一行叫一条记录,以空格为分隔符将每行切片,切开的部分叫域或者列或者字段,然后处理各个字段。
awk有3个不同版本:awk、nawk、gawk。
未作特别说明,一般指gawk,gawk是AWK的GNU版本。
awk其名称源自它的创始人Alfred Aho 、Peter Weinberger和Brian Kernighan姓氏的首个字母。
一、调用awk的方法
1.命令行方式
awk [options] 'commands' input-file(s)
2.脚本方式
将所有的awk命令写入一个文件,并使其可执行,然后将awk命令解释器作为脚本的首行,通过键入脚本名称来调用。
相当于shell脚本首行的#!/bin/sh换成#!/bin/awk
二、命令行方式用法
(一)语法格式
用法:awk [选项] -f 脚本文件 [--] 文件...
用法:awk [选项] [--] '程序' 文件...
(二)选项
-f 脚本文件, --file=脚本文件
-F fs, --field-separator=fs
-v var=val, --assign=var=val
-b, --characters-as-bytes
-c, --traditional
-C, --copyright
-d[文件], --dump-variables[=文件]
-D[文件], --debug[=文件]
-e '程序文本', --source='程序文本'
-E 文件, --exec=文件
-g, --gen-pot
-h, --help
-i 包含文件, --include=包含文件
-I, --trace
-l 库, --load=库
-L[fatal|invalid|no-ext], --lint[=fatal|invalid|no-ext]
-M, --bignum
-N, --use-lc-numeric
-n, --non-decimal-data
-o[文件], --pretty-print[=文件]
-O, --optimize
-p[文件], --profile[=文件]
-P, --posix
-r, --re-interval
-s, --no-optimize
-S, --sandbox
-t, --lint-old
-V, --version
(三)参数
1.程序
也叫脚本命令
格式为:
'模式{操作}'
它由两部分组成,分别为模式和操作。模式也可以叫做条件或者地址。
整个脚本命令是用单引号’'括起。为了防止shell解析特殊字符。
如果是执行多条脚本命令,格式为:
'模式1{命令1};模式2{命令2}...'
(1)模式
用来指定要处理的行。
模式可以是以下任意一个:
/正则表达式/:使用ERE。
关系表达式:使用运算符操作,可以是字符串或数字的比较测试。
包含表达式:使用运算符~(包含)和!~(不包含)。
BEGIN:如果模式是BEGIN,说明后面的操作只在程序开始时执行一次。
END:如果模式是END,说明后面的操作只在程序结束时执行一次。
模式1, 模式2:从匹配模式1的行到匹配模式2的行(包含该行)。
省略:表示处理所有行。
条件类型 | 条 件 | 说 明 |
---|---|---|
awk保留字 | BEGIN | 在 awk 程序一开始,尚未读取任何数据之前执行。BEGIN 后的动作只在程序开始时执行一次 |
awk保留字 | END | 在 awk 程序处理完所有数据,即将结束时执行。END 后的动作只在程序结束时执行一次 |
关系运算符 | > | 大于 |
< | 小于 | |
>= | 大于等于 | |
<= | 小于等于 | |
== | 等于。用于判断两个值是否相等 | |
!= | 不等于 | |
A~B | 判断字符串 A 中是否包含能匹配 B 表达式的子字符串 | |
A!~B | 判断字符串 A 中是否不包含能匹配 B 表达式的子字符串 | |
正则表达式 | /正则/ | 匹配正则表达式的行 |
例如:
x>10:判断变量 x 是否大于10;
x == y:判断变量 x 是否等于变量 y;
A~B:判断字符串 A 中是否包含能匹配 B 表达式的子字符串;
A!~B:判断字符串 A 中是否不包含能匹配 B 表达式的子字符串;
(2)操作
操作由一个或多个命令、函数、表达式组成,之间由换行符或分号隔开,并位于大括号内,大括号不是必须的,但它们用于根据特定的模式对一系列指令分组,主要部分是:
变量或数组赋值
输入输出
内置函数
控制流语句
2.一个或多个文件
(四)内置变量
变量 | 描述 |
---|---|
$n | 当前记录的第n个字段,字段间由FS分隔,n从1开始 |
$0 | 完整的输入记录 |
ARGC | 命令行参数的数目 |
ARGIND | 命令行中当前文件的位置(从0开始算) |
ARGV | 包含命令行参数的数组 |
CONVFMT | 数字转换格式(默认值为%.6g)ENVIRON环境变量关联数组 |
ERRNO | 最后一个系统错误的描述 |
FIELDWIDTHS | 字段宽度列表(用空格键分隔) |
FILENAME | 当前文件名 |
FNR | 各文件分别计数的行号 |
FS | 字段分隔符(默认是任何空格) |
IGNORECASE | 如果为真,则进行忽略大小写的匹配 |
NF | 一条记录的字段的数目 |
NR | 已经读出的记录数,就是行号,从1开始 |
OFMT | 数字的输出格式(默认值是%.6g) |
OFS | 输出字段分隔符,默认值与输入字段分隔符一致。 |
ORS | 输出记录分隔符(默认值是一个换行符) |
RLENGTH | 由match函数所匹配的字符串的长度 |
RS | 记录分隔符(默认是一个换行符) |
RSTART | 由match函数所匹配的字符串的第一个位置 |
SUBSEP | 数组下标分隔符(默认值是/034) |
(五)示例用法
1.使用内置函数
最常用的内置函数就是输出函数。awk中同时提供了print和printf两种打印输出的函数。
(1)print
是默认命令。
print函数的参数可以是变量、数值、字符串。字符串必须用双引号引用,参数用逗号分隔。如果没有逗号,参数就串联在一起而无法区分。
print函数的输出会把各参数用空格分开。print函数默认会在每行后面加上ORS。
如,
'{print key, "=" value}'
(2)printf
printf函数,其用法和c语言中printf基本相似,可以格式化字符串,输出复杂时,printf更加好用,代码更易懂。需要特别注意的是使用printf时默认是不会换行的,需要手动加\n。
printf 语句的格式如下:
printf(format, value1, value2, ..., valuen)
其中format是格式字符串,包含要原样打印的文本和规格(specification)。
一个规格是一个%符后面跟着一些字符,用来控制一个 value的格式。
第一个规格说明如何打印value1,第二个说明如何打印value2,依次类推。因此,有多少value要打印,在format中就要有多少个规格。
规格 | 说明 |
---|---|
%d | 十进制有符号整数 |
%u | 十进制无符号整数 |
%f | 浮点数 |
%s | 字符串 |
%c | 单个字符 |
%p | 指针的值 |
%e | 指数形式的浮点数 |
%x | 无符号以十六进制表示的整数 |
%o | 无符号以八进制表示的整数 |
%g | 自动选择合适的表示法 |
如,
'{ printf("total pay for %s is $%.2f\n", $1, $2 * $3) }'
输出样例:
total pay for Beth is $0.00
total pay for Dan is $0.00
total pay for Kathy is $40.00
total pay for Mark is $100.00
total pay for Mary is $121.00
total pay for Susie is $76.50
第一个规格是%s,说明以字符串的方式打印第一个值 $1。
第二个是%.2f,说明以数字的方式打印第二个值$2*$3,并保留小数点后面两位。
规格字符串中其他东西,包括美元符号,原样打印。
字符串尾部的\n代表开始新的一行,使得后续输出将从下一行开始。
'{ printf("%-8s $%6.2f\n", $1, $2 * $3) }'
Beth $ 0.00
Dan $ 0.00
Kathy $ 40.00
Mark $100.00
Mary $121.00
Susie $ 76.50
第一个规格%-8s将一个姓名以字符串形式在8个字符宽度的字段中左对齐输出。
第二个规格%6.2f将薪酬以数字的形式,保留小数点后两位,在6个字符宽度的字段中输出。
(3)length函数
length([string])返回字符串的长度。比如:length("abc")返回3
length()返回
$0的长度。
从文件中找出长度大于 80 的行:
awk 'length()>80' log.txt
2.使用外部命令
(1)使用system函数执行外部命令
①语法格式
awk '{system("command")}' file
system的返回值是command执行的返回值
command必须使用双引号引起来。
②示例
awk 'BEGIN{system("ls")}' file.txt
(2)使用管道
awk中的管道概念和shell的管道类似,都是使用"|"符号。如果在awk程序中打开了管道,必须先关闭该管道才能打开另一个管道。也就是说一次只能打开一个管道。管道一旦打开,就会保持打开状态直至awk退出。
又分为两种:
一种是配合getline
一种是配合print
①配合getline
1)格式
awk '{"command" | getline [var]}' file
var是可选参数,用于存储命令执行后的标准输出,默认情况下结果输出到$0
command必须用双引号引起来。
执行的结果缓存于pipe中,再传送给awk处理,如果有多行数据,awk的getline命令可能调用多次。
2)示例
awk '{"curl www.baidu.com" | getline result; print(result);}' file.txt
$awk 'BEGIN{ while(("ls" | getline d) > 0) print d}' file.txt
②配合print或printf
1)格式
awk '{print ... | "command"}' file
command必须用双引号引用起来。
output是先缓存在pipe中,等输出完毕后再调用shell命令处理,shell命令只处理一次,而且处理的时机是“awk程序结束时,或者管道关闭时(需要显式的关闭管道)”
2)示例
$ awk 'BEGIN{print "foobar" | "echo eew"}' file.txt
eew
$ awk 'BEGIN{count=0} /west/{count++} {printf("%s %stt%-15sn", $3,$4,$1) | "sort +1"} END{close("sort +1"); printf("The number of sales pers in the western"); print "region is " count "."}' file.txt
printf函数用于将输出格式化并发送给管道。所有输出集齐后一同发送给sort命令。
必须用与打开时完全相同的命令来关闭管道(sort +1),否则END块中的语句将与前面的输出一起被排序。
此处的sort命令只执行一次。
awk调用外部命令都是新开一个shell,所以要注意当前shell变量与新开shell变量问题
root@ubuntu:~# abc=12345567890
root@ubuntu:~# awk 'BEGIN{system("echo $abc")}'
root@ubuntu:~#
root@ubuntu:~# export abc=12345567890
root@ubuntu:~# awk 'BEGIN{system("echo $abc")}'
12345567890
root@ubuntu:~#
root@ubuntu:~# abc=1234567890
root@ubuntu:~# awk 'BEGIN{print "echo","$abc"| "/bin/bash"}'
root@ubuntu:~#
root@ubuntu:~# export abc=1234567890
root@ubuntu:~# awk 'BEGIN{print "echo","$abc"| "/bin/bash"}'
1234567890
root@ubuntu:~#
3.使用内置变量
(1)变量NF
表示当前行有多少个字段,字段从1开始编号。
$NF就代表最后一个字段。
$ echo 'this is a test' | awk '{print $NF}'
test
$(NF-1)代表倒数第二个字段。
$ awk -F ':' '{print $1, $(NF-1)}' demo.txt
root /root
daemon /usr/sbin
bin /bin
sys /dev
sync /bin
(2)删除列
比如,删除第2列,让它等于空
awk '{$2 = ""; print $0}' file
awk '{$2 = null; print $0}' file
但是这种写法会多出一个空格,如下例:
[root@centos79 test3]# cat a.txt
3 5 6 2
s g 3 5
c f h e
[root@centos79 test3]# awk '{$2 = ""; print $0}' a.txt
3 6 2
s 3 5
c h e
删除第二列,不留空格的写法如下
awk '{$2 = "\b"; print $0}' file
如下例:
[root@centos79 test3]# cat a.txt
3 5 6 2
s g 3 5
c f h e
[root@centos79 test3]# awk '{$2 = "\b"; print $0}' a.txt
3 6 2
s 3 5
c h e
(3)统计/etc/passwd文件名,每行的行号,每行的列数
#awk -F':' '{print "filename:" FILENAME ",linenumber:" NR ",columns:" NF}' /etc/passwd
filename:/etc/passwd,linenumber:1,columns:7
filename:/etc/passwd,linenumber:2,columns:7
filename:/etc/passwd,linenumber:3,columns:7
filename:/etc/passwd,linenumber:4,columns:7
(4)使用自定义变量
awk中自定义变量不需要声明,默认初始值是0,所以不用初始化,可以直接使用。
例子:计算文件大小
$ ls -l *.txt | awk '{sum+=$5} END {print sum}'
666581
4.直接使用shell变量。
(1)是“双引号+单引号+shell变量+单引号+双引号”的格式
awk '{print "'$shellvar'"}' filename
$ foo=bar
$ awk 'BEGIN{print "'$foo'"}' file.txt
bar
(2)是“双引号+单引号+双引号+shell变量+双引号+单引号+双引号”的格式
awk '{print "'"$shellvar"'"}' filename
如果变量的值中包含空格,为了shell不把空格作为分隔符,则应使用这种方法
(3)export变量,然后在awk中使用ENVIRON[“var”]形式获取环境变量的值
awk '{print ENVIRON["var"]}' filename
$ export foo=bar
$ awk 'BEGIN{print ENVIRON["foo"]}' file.txt
bar
(4)使用-v选项
定义变量时,让其值等于shell变量的值
示例:
foo=bar
awk -v awkfoo=$foo '{print awkfoo}' file.txt
实例:
$ awk -va=1 '{print $1,$1+a}' log.txt
---------------------------------------------
2 3
3 4
This's 1
10 11
$ awk -va=1 -vb=s '{print $1,$1+a,$1b}' log.txt
---------------------------------------------
2 3 2s
3 4 3s
This's 1 This'ss
10 11 10s
5.-F选项
默认情况下,awk会使用空格分割一行数据,但是可以使用-F选项指定分隔符。-F后面的分隔符可以用双引号,也可以用单引号,也可以不用引号。
Num,Name,Company,Product
1,Jobs,Apple,iPhone
2,Jack,Alibaba,taobao
3,Pony,Tencent,wechat
awk -F"," '{print $2"\t"$3}' form.txt
Name Company
Jobs Apple
Jack Alibaba
Pony Tencent
这里的选项 -F “,” 就表示以每行数据中的逗号为分隔符,来切分数据。
实例:
#使用","分割
$ awk -F, '{print $1,$2}' log.txt
2 this is a test
3 Are you like awk
This's a test
10 There are orange apple
# 或者使用内建变量
$ awk 'BEGIN{FS=","} {print $1,$2}' log.txt
2 this is a test
3 Are you like awk
This's a test
10 There are orange apple
# 使用多个分隔符
$ awk -F '[ ,]' '{print $1,$2,$5}' log.txt
2 this test
3 Are awk
This's a
10 There apple
6.-f选项从文件中读取程序
awk 允许将脚本命令存储到文件中,然后再在命令行中引用,比如:
假设有这么一个文件(学生成绩表):
$ cat score.txt
Marry 2143 78 84 77
Jack 2321 66 78 45
Tom 2122 48 77 71
Mike 2537 87 97 95
Bob 2415 40 57 62
我们的 awk 脚本如下:
$ cat cal.awk
#!/bin/awk
#运行前
BEGIN {
math = 0
english = 0
computer = 0
printf "NAME NO. MATH ENGLISH COMPUTER TOTAL\n"
printf "---------------------------------------------\n"
}
#运行中
{
math+=$3
english+=$4
computer+=$5
printf "%-6s %-6s %4d %8d %8d %8d\n", $1, $2, $3,$4,$5, $3+$4+$5
}
#运行后
END {
printf "---------------------------------------------\n"
printf " TOTAL:%10d %8d %8d \n", math, english, computer
printf "AVERAGE:%10.2f %8.2f %8.2f\n", math/NR, english/NR, computer/NR
}
我们来看一下执行结果:
$ awk -f cal.awk score.txt
NAME NO. MATH ENGLISH COMPUTER TOTAL
---------------------------------------------
Marry 2143 78 84 77 239
Jack 2321 66 78 45 189
Tom 2122 48 77 71 196
Mike 2537 87 97 95 279
Bob 2415 40 57 62 159
---------------------------------------------
TOTAL: 319 393 350
AVERAGE: 63.80 78.60 70.00
7.BEGIN END模式
awk 'BEGIN{ commands } pattern{ commands } END{ commands }'
第一步:执行BEGIN{ commands }语句块中的语句;
第二步:从文件或标准输入(stdin)读取一行,然后执行pattern{ commands }语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。
第三步:当读至输入流末尾时,执行END{ commands }语句块。
BEGIN语句块在awk开始从输入流中读取行之前执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中。
END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块。
pattern语句块中的通用命令是最重要的部分,它也是可选的。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块。
示例
echo -e "A line 1\nA line 2" | awk 'BEGIN{ print "Start" } { print } END{ print "End" }'
Start
A line 1
A line 2
End
8.条件表达式模式
(1)下面用一个综合的例子来说明awk的条件判断和数值计算,有这样一组数据保存为pay.txt:
Name 1st 2nd 3rd
VBird 23000 24000 25000
DMTsai 21000 20000 23000
Bird2 43000 42000 41000
现在想加一列"Total",计算每一行的数值总和。
用awk可以完成这个需求:
awk 'NR==1 {printf "%10s %10s %10s %10s %10s \n",$1,$2,$3,$4,"Total"};NR>1 {printf "%10s %10s %10s %10s %10s \n",$1,$2,$3,$4,$2+$3+$4}' pay.txt
(2)过滤第一列大于2的行
$ awk '$1>2' log.txt
#输出
3 Are you like awk
This's a test
10 There are orange,apple,mongo
(3)过滤第一列等于2的行
$ awk '$1==2 {print $1,$3}' log.txt
#输出
2 is
(4)过滤第一列大于2并且第二列等于’Are’的行
$ awk '$1>2 && $2=="Are" {print $1,$2,$3}' log.txt
#输出
3 Are you
9.使用循环语句
(1)输出每行的前5列,并按行输出
awk '{for(i=1;i<6;i++)printf("%s ",$i);printf("\n")}' input.file
(2)输出多列,并更改分隔符为TAB
awk '{for(i=2;i<=NF;i++)printf("%s\t",$i);printf("\n")}' input.file
(3)每三行作为一列输出文章来源:https://www.toymoban.com/news/detail-493560.html
awk '{if(NR%3 != 0){ORS="\t"}else{ORS="\n"}print}' input.file
(4)打印九九乘法表文章来源地址https://www.toymoban.com/news/detail-493560.html
seq 9 | sed 'H;g' | awk -v RS='' '{for(i=1;i<=NF;i++)printf("%dx%d=%d%s", i, NR, i*NR, i==NR?"\n":"\t")}'
到了这里,关于awk命令的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!