目录
序:回顾shell三剑客的关系
shell三剑客最擅长解决的问题
- sed 解决文件修改的问题
- grep 解决查找的问题
- awk 解决截取的问题
一、awk基础
1.1awk简介
1.1.1awk历史简介
a.现在用的是1985年的版本
b. 分支版本
- nawk:八十年代的更新
- mawk、gawk:GNU项目的
1.1.2awk简单说明
a.专门为文本处理而设计的编程语言
b.用于数据提取和报告(生成格式报告)的工具
c.是数据流驱动的脚本语言
- 包含一组针对文本数据流的操作,可以直接对文本文件进行处理。
- 可以从管道符获得数据。
d.擅长处理数据库和表型文件
e.awk是一种解释性语言
f.类似于C语言的语法
g.能用很短的程序对文档资料进行修改、比较、提取、打印输出
h.本博以awk的gawk为例,默认情况下centos使用的就是gawk
[root@localhost ~]# ll /usr/bin/awk
lrwxrwxrwx. 1 root root 4 Jan 2 17:02 /usr/bin/awk -> gawk
1.1.3awk的特性和优势
a.有许多内置函数与内置变量。
b.默认使用的扩展正则表达式。
对比:sed -r,grep -E 使用扩展正则表达式,而sed和grep工具默认使用的是基本正则表达式
k.awk与sed类似,从输入流里面一行一行读取内容,并通过某种规则提取出行后规范化输出。所以涉及到了记录分隔符的概念(也就是如果去定义一行应包含哪些内容)。
1.2基本语法
1.2.1 awk程序自动会完成的工作
- 读取输入行
- 切分字段(使用字段分隔符)
- 存储管理
- 初始化
- 若使用了用户自定义变量不需要声明变量类型,awk内置字符串类型和数值类型变量。
类比数据库里面的概念,读取的每一行我们称为记录,而记录中以分隔符分隔的为字段。
1.2.2 语法格式
awk [OPTIONS] -f PROGRAM_FILE [--] filename_list
awk [OPTIONS] [--] PROGRAM filename_list
1.2.3 简单理解PROGRAM
可以将PROGRAM理解成sed中的script,PROGRAM由多个PATTERN和ACTION组成,PATTERN即为以某种形式(如正则表达式)去判断目前该行是否是我所需要处理的行的匹配规则,而ACTION则是匹配成功后需要执行的动作。
#PROGRAM的常用通用格式:
awk [OPTIONS] [--] 'pattern{action};pattern{action};' filename_list
1.2.4 简单理解字段分隔符
awk搜索文件并把每一个输入行按照awk原定义好的字段分隔符FS将记录切分成字段(field)。其中字段分隔符则是每一个awk程序的内置变量之一。当然,awk程序还内置了其他字符串和数值类型的内置变量,用于便捷地表示某些特定的量(例如字段分隔符、记录分隔符等)。
#例如处理/etc/passwd文件通常会这么处理
[root@localhost ~]# awk -F: '{print $1,$4}' /etc/passwd
root 0
bin 1
daemon 2
adm 4
lp 7
sync 0
shutdown 0
另外,awk还支持用户自定义变量而不需要声明变量的数据类型(因为awk程序内置了字符串和数值类型)。
#通常使用参数-v var=val的形式指定
1.2.5 简单理解字段变量
例如:$3是位置参数,代表第三个字段
[tyson@localhost learnawk]$ awk '$3 > 0 { print $1,$2 * $3 }' firstprogram.txt
Kathy 40
Mark 100
Mary 121
Susie 76.5
[tyson@localhost learnawk]$ cat firstprogram.txt
Beth 4.00 0
Dan 3.75 0
Kathy 4.00 10
Mark 5.00 20
Mary 5.50 22
Susie 4.25 18
注意:
awk中的命令部分(PROGRAM部分)用单引号包围,否则是用shell程序来解析。
用引号包围命令部分是为了告诉系统让awk程序来解析该命令,单引号中包围的内容是一个完整的awk程序。
1.2.6 简单理解filname_list含义
可以是多个文件
[tyson@localhost learnawk]$ awk ' $3>0 {print$1,$2 * $3}' firstprogram.txt firstprogram2.txt
Kathy 40
Mark 100
Mary 121
Susie 76.5
Kathy 40
Mark 100
Mary 121
Susie 76.5
1.2.7 第一个awk程序
[tyson@localhost testawk]$ cat text.txt
SR 8649 275 Asia
Canada 3852 25 North_America
China 3705 1032 Asia
USA 3615 237 North_America
Brazil 3286 134 South_America
India 1267 746 Asia
Mexico 762 78 North_America
France 211 55 Europe
Japan 144 120 Asia
Germany 96 61 Europe
England 94 56 Europe
[tyson@localhost testawk]$ awk '$2>3000{ print $2*$3 }' text.txt
2378475
96300
3823560
856755
440324
该awk命令中的program部分(被单引号括起来的部分)是仅有一个pattern{action}对组成的完整程序。
其中:
- PATTERN是$2>3000,模式匹配后执行ACTION
- ACTION是print $2*$3,打印第二和第三个字段的乘积
二 awk中OPTIONS的说明
2.0 参数列表
常用部分 | |
-f program_file_list | 指定包含了awk命令的文件,通常以.awk后缀命名,可以指定多个 |
-F fs | 自定义字段分隔符,用于将每一条记录分割成字段(field)。默认是空格 |
-v var=val | 在awk程序运行之前为自定义的变量赋值。该值可以用到BEGIN块的的ACTION部分中。 |
-d[file] | 将一些awk程序内置变量的最终值排序成列表并打印到文件中(默认在同目录下生成awkvars.out文件,当然也可以直接指定) |
-h --help | 获取帮助 |
-P | 待补充 |
-S | 待补充 |
-V | 显示版本 |
2.1 -f program-file
指定包含了awk命令的文件,不是从命令行参数中去读取。可以通过-f选项指定多个包含awk命令文件。
[tyson@Tyson Lee testawk]$ awk -f fortext.awk text.txt
Country Aera 0 CONTENT
SR 8649 275 Asia
Canada 3852 25 North_America
China 3705 1032 Asia
USA 3615 237 North_America
Brazil 3286 134 South_America
India 1267 746 Asia
Mexico 762 78 North_America
France 211 55 Europe
Japan 144 120 Asia
Germany 96 61 Europe
England 94 56 Europe
we are donw
[tyson@Tyson Lee testawk]$ cat fortext.awk
BEGIN{
FS=" "
printf("%10s %6s %5d %s\n\n","Country","Aera","POP","CONTENT")
}
{
printf("%10s %6d %5d %s\n",$1,$2,$3,$4)
}
END{
printf("\nwe are donw\n")
}
2.2 -F fs
-F选项可以自定义字段分隔符,既我们可以改变FS变量对应的值来满足需求。
fs是一个字符串或正则表达式。
例如:使用awk程序对passwd文件进行处理。注意在awk程序中使用变量(内置变量、自定义变量)是不需要用符号“$”引用的,而使用字段变量是需要用符号"$"引用的,这个在接下来的变量部分会提到。
[tyson@Tyson Lee testawk]$ awk -F: '{print $1,$3*$4}' /etc/passwd |grep tyson
tyson 1002001
tyson1 1006009
[tyson@Tyson Lee testawk]$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
…………………………………………………………………………………………………………
默认情况下awk中FS的值是空格,OFMT的含义是数值输出格式。
#后面打印了另一内置变量用以标明FS有输出空格而不是输出空内容
[tyson@Tyson Lee testawk]$ awk 'BEGIN{print FS,OFMT}'
%.6g
2.3 -v var =val
程序开始之前将val这个值赋给var这个变量,通过-v指定的变量可以用于awk程序的BEGIN块中
简略地说就是赋值一个用户自定义变量。
[tyson@Tyson Lee testawk]$ awk -v testVar=$USER 'BEGIN{print testVar}'
tyson
2.4 -d [file]
--dump-variables
将一些awk程序内置变量的最终值排序成列表并打印到文件中(默认在同目录下生成awkvars.out文件,当然也可以直接指定)
[tyson@localhost testawk]$ ll
total 8
-rw-rw-r--. 1 tyson tyson 103 Jan 10 20:36 big
-rw-rw-r--. 1 tyson tyson 304 Jan 10 19:51 text.txt
[tyson@localhost testawk]$ awk -d '$2>3000{ print $2*$3 }' text.txt
2378475
96300
3823560
856755
440324
[tyson@localhost testawk]$ ll
total 12
-rw-rw-r--. 1 tyson tyson 298 Jan 12 14:55 awkvars.out
-rw-rw-r--. 1 tyson tyson 103 Jan 10 20:36 big
-rw-rw-r--. 1 tyson tyson 304 Jan 10 19:51 text.txt
[tyson@localhost testawk]$ cat awkvars.out
ARGC: 2
ARGIND: 1
ARGV: array, 2 elements
BINMODE: 0
CONVFMT: "%.6g"
ERRNO: ""
FIELDWIDTHS: ""
FILENAME: "text.txt"
FNR: 11
FPAT: "[^[:space:]]+"
FS: " "
IGNORECASE: 0
LINT: 0
NF: 4
NR: 11
OFMT: "%.6g"
OFS: " "
ORS: "\n"
RLENGTH: 0
RS: "\n"
RSTART: 0
RT: "\n"
SUBSEP: "\034"
TEXTDOMAIN: "messages"
2.5 -h
--help
获取awk程序选项说明
POSIX options: GNU long options: (standard)
-f progfile --file=progfile
-F fs --field-separator=fs
-v var=val --assign=var=val
Short options: GNU long options: (extensions)
-b --characters-as-bytes
-c --traditional
-C --copyright
-d[file] --dump-variables[=file]
-e 'program-text' --source='program-text'
-E file --exec=file
-g --gen-pot
-h --help
-L [fatal] --lint[=fatal]
-n --non-decimal-data
-N --use-lc-numeric
-O --optimize
-p[file] --profile[=file]
-P --posix
-r --re-interval
-S --sandbox
-t --lint-old
-V --version
2.6 -P 待补充
2.7 -S 待补充
2.8 -V 显示版本
[tyson@Tyson Lee testawk]$ awk -V
GNU Awk 4.0.2
三 awk中PROGRAM的说明
3.1 通俗理解
awk -d '$2>3000{ print $2*$3 }' text.txt
-
这个例子中,引号内的就是PROGRAM部分,也就是awk命令的程序部分。
-
可以将PROGRAM理解成sed中的script,PROGRAM由多个PATTERN和ACTION组成,PATTERN即为以某种形式(如正则表达式)去判断目前该行是否是我所需要处理的行的匹配规则,而ACTION则是匹配成功后需要执行的动作。
3.2 PROGRAM的语法格式
每一个PROGRAM部分都是由一个或多个PATTER-ACTION(模式-动作)组成的序列。而不同的调用方式有不同的写法规范。
#1.PROGRAM写在一行
awk 'pattern{action};pattern{action};pattern{action}…………' test.txt
#2.PROGRAM写在文件内
#/test/program.awk
patter { action }
patter { action }
………………
patter { action }
patter { action }
#若PROGRAM写在文件内则awk命令需要通过这种形式指定包含awk命令的文件
awk -f program.awk test.txt
3.3 PROGRAM中两部分的关系
parogram中pattern部分和action部分可以只存在其一
-
action部分省略,默认只将每一行print。
-
pattern部分被省略,代表着每一行都匹配成功,都会执行后面的action(s)。
3.4 awk中对每一行执行PROGRAM的流程
-
读取一行,对该行执行pattern-action(也就是PROGRAM部分)。
-
对读取的行进行扫描搜索,搜索行中是否有内容被pattern匹配。
-
如果行含有被pattern命中的部分则该行标记为匹配成功。
-
该行匹配成功:执行action部分。(若未指定动作会有自动输出)
-
若匹配不成功:则不执行action部分也不执行自动输出。
-
读取下一行。
3.5 详解PROGRAM中PATTERN部分
3.5.1 pattern的作用说明
awk程序的pattern与sed的寻址同理,既判断目前读取的该行是否是我们所指定的匹配方式所能匹配成功的一行。也就是说只有与所指定的pattern成功匹配才会对该行执行后面的action,若省略patter则是对每一行都执行action。
3.5.2 与sed寻址的比较
awk的pattern支持的规则:正则表达式、字符串与数值的比较、流程控制语句。
相较而言,sed中script的寻址部分只支持正则表达式。
3.5.3 模式表示方式汇总
常用 | |
BEGIN | |
END | |
普通表达式 | |
/regular expression/ | 正则表达式 |
进阶 | |
关系表达式 | |
模式与 | |
模式或 | |
选择表达式 | pattern1?pattern2:pattern3 |
模式分组 | (pattern) |
模式取反 | !pattern |
模式范围 | pattern1,pattern2 |
较少使用 | |
BEGINFILE | |
ENDFILE |
3.5.3.4 模式之BEGIN与END
BEGIN :在awk读取第一个文件第一行前要执行的awk程序(处理输入流之前)。
END:在awk处理完最后一个文件最后一行后要执行的awk程序(处理完成输入流全部内容之后)。
3.5.3.4.1 说明
a.他们不匹配任何输入行
-
控制初始化和扫尾,不可以与其他模式表达式进行组合
-
不能缺失action部分。
b.若有多个BEGIN或END这种PATTERN,则按顺序执行,不是并行。
c.通常将BEGIN放在PROGRAM部分的开头,END放在PROGRAM部分的结尾。
3.5.3.4.2 常见作用
BEGIN常用来更改字段分隔符FS。
END用来输出一些汇总信息。
3.5.3.4.3 案例
首先需要注意几点:
-
awk中的action部分使用的是类c语言的语法,而不是shell语法。
-
不需要初始化变量。
-
父shell的环境变量不会应用到awk程序。但是没有在引号内使用变量则这个环境变量仍能够被识别出来。
-
在PROGRAM中使用内置变量与用户自定义变量都不需要使用符号“$”
[tyson@Tyson Lee testawk]$ echo $PATH
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/tyson/.local/bin:/home/tyson/bin
[tyson@Tyson Lee testawk]$ echo $USER
tyson
[tyson@Tyson Lee testawk]$ awk 'BEGIN{print $USER}'
[tyson@Tyson Lee testawk]$ awk -v USER=$USER 'BEGIN{print USER}'
tyson
实战案例
[tyson@Tyson Lee testawk]$ cat fortext.awk
BEGIN{
FS=" "
printf("%10s %6s %5d %s\n\n","Country","Aera","POP","CONTENT")
}
{
printf("%10s %6d %5d %s\n",$1,$2,$3,$4)
}
END{
printf("\nwe are donw\n")
}
[tyson@Tyson Lee testawk]$ cat text.txt
SR 8649 275 Asia
Canada 3852 25 North_America
China 3705 1032 Asia
USA 3615 237 North_America
Brazil 3286 134 South_America
India 1267 746 Asia
Mexico 762 78 North_America
France 211 55 Europe
Japan 144 120 Asia
Germany 96 61 Europe
England 94 56 Europe
[tyson@Tyson Lee testawk]$ awk -f fortext.awk text.txt
Country Aera 0 CONTENT
SR 8649 275 Asia
Canada 3852 25 North_America
China 3705 1032 Asia
USA 3615 237 North_America
Brazil 3286 134 South_America
India 1267 746 Asia
Mexico 762 78 North_America
France 211 55 Europe
Japan 144 120 Asia
Germany 96 61 Europe
England 94 56 Europe
we are donw
3.5.3.5 模式之普通表达式
在模式中,可以通过普通表达式的真与假作为PATTERN来判断该行是否匹配,如:$1>$2{print $3}。但是在awk程序中,用于进行普通表达式运算的不仅仅可以是数值,还可以是字符串常量、空字符串、子字符串。
并且,awk中PATTERN的普通表达式还可以比较字符串和数值(会进行强制类型转换)。
最终若结果为0则该行不匹配,若结果为非0则该行匹配。
3.5.3.5.1 比较运算符汇总
< | |
<= | |
== | 等于 |
!= | 不等于 |
>= | |
> | |
~ | 匹配 |
!~ | 不匹配 |
需要注意的是:
-
在普通表达式中:~、!~,分别是匹配和不匹配。
-
在正则表达式中:波浪号不代表上述两个含义。
3.5.3.5.2 表达式运算汇总
操作 | 运算符 | 例子 | 例子的值 |
赋值 | = += -= *= /= %= ^= |
|
|
条件表达式 | ?: | x?y;z | x为真则表达式值为y,反之为z |
逻辑或、逻辑与 | ||、&& |
|
|
数组成员 | in | i in a | 若a[i]存在,则为表达式真 |
匹配 | ~ !~ | $1~/x/ | 若第一个字段包含字符x则表达式为真 |
关系运算 | <=、==、>=、!=、>=、> |
|
|
拼接 |
| "a""bc" | "abc" |
加减乘除和模运算 |
| ||
单目加、单目减 | + - | -x、+x |
|
指数运算 | ^ |
|
|
自增、自减 | ++、-- |
|
|
字段 | $ | $i+1 | 1+第i个字段的值 |
组合 | () | ($i)++ | 给第i个字段的值加1 |
需要具备的基础知识(科班出身应该都懂):
-
表达式是一种用语判断的语句,那么必然可以用于PATTERN中来判断该行是否匹配。除此之外,ACTION部分当然也可以使用普通表达式,例如{if($1>$2) print $3}。
-
运算符的优先级问题。
-
运算符的左右结合问题。
-
数值比较:比较大小;字符串比较:按照ASCII码大小逐位比较。
-
操作数的分类问题:awk中的操作数分常量:字符串与数值;变量:用户定义的、内建的、或字段的。
3.5.3.6 模式之正则表达式
3.5.3.6.1/regexpr/
当前行能匹配regexpr
3.5.3.6.2expression ~ /regexpr/
expression字符串值包含一段子字符串能够被regexpr匹配的子字符时,该模式匹配。(expression可以是变量常量表达式等)。
[tyson@Tyson Lee testawk]$ awk '$1~/an/' text.txt
Canada 3852 25 North_America
France 211 55 Europe
Japan 144 120 Asia
Germany 96 61 Europe
England 94 56 Europe
[tyson@Tyson Lee testawk]$ cat text.txt
SR 8649 275 Asia
Canada 3852 25 North_America
China 3705 1032 Asia
USA 3615 237 North_America
Brazil 3286 134 South_America
India 1267 746 Asia
Mexico 762 78 North_America
France 211 55 Europe
Japan 144 120 Asia
Germany 96 61 Europe
England 94 56 Europe
3.5.3.6.3 expression!~/regexpr/
expression字符串值不包含一段子字符串能够被regexpr匹配的子字符时,该模式匹配。
例如:
awk '$1 ~ /U/{print $0}' file
awk '$1 !~ /U/{print $0}' file
在两种形式中,正则表达式匹配的不是整行,而是前面表达式所指定的内容,在这里个例子就是字段1。
[tyson@Tyson Lee testawk]$ awk '$1!~/an/' text.txt
SR 8649 275 Asia
China 3705 1032 Asia
USA 3615 237 North_America
Brazil 3286 134 South_America
India 1267 746 Asia
Mexico 762 78 North_America
3.5.3.7 模式之复合模式
所谓复合模式,就是通过逻辑运算符&&、||、非组合了多个模式的模式。
pattern || pattern
pattern && pattern
#找出前两个字段同时为字母或后两个字段同时为数字的记录
[tyson@Tyson Lee testawk]$ awk '$1~/[a-zA-Z]/&&$2~/[a-zA-Z]/||$3~/[0-9]/&&$4~/[0-9]/' testFuhe.txt
a 2 3 4
a b 1 2
a b c d