一、awk介绍

  awk 是一种程序语言. 它具有一般程序语言常见的功能.
  因awk语言具有某些特点, 如 : 使用直译器(Interpreter)不需先行编译; 变量无类型之分(Typeless), 可使用文字当数组的下标(Associative Array)...等特色. 因此, 使用awk撰写程序比起使用其它语言更简洁便利且节省时间. awk还具有一些内建功能, 使得awk擅于处理具数据行(Record), 字段(Field)型态的资料; 此外, awk内建有pipe的功能, 可将处理中的数据传送给外部的 Shell命令加以处理, 再将Shell命令处理后的数据传回awk程序, 这个特点也使得awk程序很容易使用系统资源。

1.1、awk读取数据设置

awk读取输入文件时,每次读取一条记录(record)(默认情况下按行读取,所以此时记录就是行)。每读取一条记录,将其保存到$0中,然后执行一次main代码段
在读取数据时,可设置表示输入记录分隔符的预定义变量RS(Record Separator)来改变每次读取的记录模式

awk 'BEGIN{RS="\n"}'默认情况下使用\n换行符进行分隔读取。
RS="":按段落读取
RS="\0":一次性读取所有数据,但有些特殊文件中包含了空字符\0
RS="^$":真正的一次性读取所有数据,因为非空文件不可能匹配成功
RS="\n+":按行读取,但忽略所有空行

所以在读取数据前,可以改变RS来改变每次读取数据的模式

1.2、awk的执行过程

  1. 自动从指定的数据文件中读取一个数据行.
  2. 自动更新(Update)相关的内建变量之值. 如 : NF, NR, $0...
  3. 依次执行程序中 所有 的 Pattern { Actions } 指令.
  4. 当执行完程序中所有 Pattern { Actions } 时, 若数据文件中还有未读取的数据, 则反复执行步骤1到步骤4.

1.3、本文实例数据

ID      name    gender  age     email           phone
1 zhangs female 34 aaa@163.com 13423394013
2 lisi male 45 abb@gmail.com 15608492523
3 wmzi female 61 ccc@sohu.com 18848796505
4 wangw female 31 acc@sohu.com 18848796506
5 liub male 56 gdd@163.com 18848796507
6 zhangf female 34 adb@139.com 18848796573
7 guany male 56 eab@189.com 17048796508
8 zhaoy female 35 189@163.com 17058796503
9 huangz male 65 dd@189.com 13648796593
10 lvbu male 43 byy@sohu.com 18148796803
11 sunwk male 99 suk@sohu.com 18148706803

二、awk使用语法和选项

2.1、使用语法

awk [ -- ] program-text file ...        (1)
awk -f program-file [ -- ] file ... (2)
awk -e program-text [ -- ] file ... (3) program-txt是awk命令行中的awk代码,一般使用单引号包围
-f program-file表示将awk代码写在文件中,然后使用-f选项去执行该文件
-e program-text也用于指定awk代码,如果要结合文件和命令行一起使用,必须使用-e和-f,不能使用(1)

2.2、使用选项

-e program-text
--source program-text
指定awk程序表达式,可结合-f选项同时使用
在使用了-f选项后,如果不使用-e,awk program是不会执行的,它会被当作ARGV的一个参数 -f program-file
--file program-file
从文件中读取awk源代码来执行,可指定多个-f选项 -F fs
--field-separator fs
指定输入字段分隔符(FS预定义变量也可设置) -n
--non-decimal-data
识别文件输入中的8进制数(0开头)和16进制数(0x开头)
echo '030' | awk -n '{print $1+0}' -o [filename]
格式化awk代码。
不指定filename时,则默认保存到awkprof.out
指定为`-`时,表示输出到标准输出 -v var=val
--assign var=val
在BEGIN之前,声明并赋值变量var,变量可在BEGIN中使用

2.3、awk程序语法

awk程序中主要语法是 Pattern { Actions}
Pattern:为匹配模式,即判定某一行是否符合该模式,然后才执行Actions
Actions:为执行逻辑,其中多个action如果写在同一行,则需使用分号分隔

pattern和action都可以省略

  • 省略pattern,等价于对每一行数据都执行action
  • 例如:awk '{print $0}' test.txt

省略代码块{action},等价于{print}即输出所有行

  • 例如:awk '/Alice/' test.txt等价于awk '/Alice/{print $0}' test.txt
  • 省略代码块中的action,表示对筛选的行什么都不做
  • 例如:awk '/Alice/{}' test.txt

pattern{action}任何一部分都可以省略

  • 例如:awk '' test.txt

pattern中使用逻辑与、非、或

pattern && pattern # 逻辑与 3>2 && 3>1 {action}
pattern || pattern # 逻辑或 3>2 || 3<1 {action}
! pattern # 逻辑取反 !/a.*ef/{action}

常用的Pattern

# 1.根据行号筛选
NR==2 # 筛选出第二行
NR>=2 # 输出第2行和之后的行 # 2.根据正则表达式筛选整行
/qq.com/ # 输出带有qq.com的行
$0 ~ /qq.com/ # 等价于上面命令
/^[^@]+$/ # 输出不包含@符号的行
!/@/ # 输出不包含@符号的行 # 3.根据字段来筛选行
($4+0) > 24{print $0} # 输出第4字段大于24的行
$5 ~ /qq.com/ # 输出第5字段包含qq.com的行 # 4.将多个筛选条件结合起来进行筛选
NR>=2 && NR<=7 #输出行数大于等于2并且小于等于7
$3=="male" && $6 ~ /^170/ #输出第三个字段等于male并且第六个字段不用170开头
$3=="male" || $6 ~ /^170/ #输出第三个字段等于male或者第六个字段不用170开头 # 5.按照范围进行筛选 flip flop
# pattern1,pattern2{action}
NR==2,NR==7 # 输出第2到第7行
NR==2,$6 ~ /^170/ # 输出第2行到匹配第六个字段170开头的行

action有很多,例如:

awk的 I/O指令 : print, printf( ), getline...
awk的 流程控制指令 : if(...){..} else{..}, while(...){...}...

实例:

[root@lgh test]# awk '' test.txt #action和pattern都省了
[root@lgh test]# awk '/163/{print $0}' test.txt #匹配行中有163的行,并输出
1 zhangs female 34 aaa@163.com 13423394013
5 liub male 56 gdd@163.com 18848796507
8 zhaoy female 35 189@163.com 17058796503
[root@lgh test]# awk '/163/' test.txt #省略action,默认输出$0
1 zhangs female 34 aaa@163.com 13423394013
5 liub male 56 gdd@163.com 18848796507
8 zhaoy female 35 189@163.com 17058796503
[root@lgh test]# awk '{print $0}' test.txt #省略pattern,输出全部行
ID name gender age email phone
1 zhangs female 34 aaa@163.com 13423394013
2 lisi male 45 abb@gmail.com 15608492523
3 wmzi female 61 ccc@sohu.com 18848796505
4 wangw female 31 acc@sohu.com 18848796506
5 liub male 56 gdd@163.com 18848796507
6 zhangf female 34 adb@139.com 18848796573
7 guany male 56 eab@189.com 17048796508
8 zhaoy female 35 189@163.com 17058796503
9 huangz male 65 dd@189.com 13648796593
10 lvbu male 43 byy@sohu.com 18148796803
11 sunwk male 99 suk@sohu.com 18148706803

三、awk内置变量

预定义变量分为两类:控制awk工作的变量和携带信息的变量

第一类:控制AWK工作的预定义变量

$0,$1,$2,...$NF:其中$0表示整行数据,$1,$2,...,$NF分别表示该行的第一个字段、第二个字段,...,最后一个字段
RS:输入记录分隔符,默认为换行符\n
IGNORECASE:默认值为0,表示所有的正则匹配不忽略大小写。设置为非0值(例如1),之后的匹配将忽略大小写。例如在BEGIN块中将其设置为1,将使FS、RS都以忽略大小写的方式分隔字段或分隔record
FS:读取记录后,划分为字段的字段分隔符。
FIELDWIDTHS:以指定宽度切割字段而非按照FS。
FPAT:以正则匹配匹配到的结果作为字段,而非按照FS划分。
OFS:print命令输出各字段列表时的输出字段分隔符,默认为空格" "
ORS:print命令输出数据时在尾部自动添加的记录分隔符,默认为换行符\n
CONVFMT:在awk中数值隐式转换为字符串时,将根据CONVFMT的格式按照sprintf()的方式自动转换为字符串。默认值为”%.6g
OFMT:在print中,数值会根据OFMT的格式按照sprintf()的方式自动转换为字符串。默认值为”%.6g

第二类:携带信息的预定义变量

ARGC和ARGV:awk命令行参数的数量、命令参数的数组。
ARGIND:awk当前正在处理的文件在ARGV中的索引位置。所以,如果awk正在处理命令行参数中的某文件,则ARGV[ARGIND] == FILENAME为真
FILENAME:awk当前正在处理的文件(命令行中指定的文件),所以在BEGIN中该变量值为空
ENVIRON:保存了Shell的环境变量的数组。例如ENVIRON["HOME"]将返回当前用户的家目录
NR:当前已读总记录数,多个文件从不会重置为0,所以它是一直叠加的,可以直接修改NR,下次读取记录时将在此修改值上自增
FNR:当前正在读取文件的第几条记录,每次打开新文件会重置为0,可以直接修改FNR,下次读取记录时将在此修改值上自增
NF:当前记录的字段数,
RT:在读取记录时真正的记录分隔符,
RLENGTH:match()函数正则匹配成功时,所匹配到的字符串长度,如果匹配失败,该变量值为-1
RSTART:match()函数匹配成功时,其首字符的索引位置,如果匹配失败,该变量值为0
SUBSEP:arr[x,y]中下标分隔符构建成索引时对应的字符,默认值为\034,是一个不太可能出现在字符串中的不可打印字符。

1、$0,$1,$2,...,$NF

[root@lgh test]# awk '{print $0,$1,$2,$NF}' test.txt #输出整行,然后输出第一,第二,和最后一个字段的值
ID name gender age email phone ID name phone
1 zhangs female 34 aaa@163.com 13423394013 1 zhangs 13423394013
2 lisi male 45 abb@gmail.com 15608492523 2 lisi 15608492523
3 wmzi female 61 ccc@sohu.com 18848796505 3 wmzi 18848796505
4 wangw female 31 acc@sohu.com 18848796506 4 wangw 18848796506
5 liub male 56 gdd@163.com 18848796507 5 liub 18848796507
6 zhangf female 34 adb@139.com 18848796573 6 zhangf 18848796573
7 guany male 56 eab@189.com 17048796508 7 guany 17048796508
8 zhaoy female 35 189@163.com 17058796503 8 zhaoy 17058796503
9 huangz male 65 dd@189.com 13648796593 9 huangz 13648796593
10 lvbu male 43 byy@sohu.com 18148796803 10 lvbu 18148796803
11 sunwk male 99 suk@sohu.com 18148706803 11 sunwk 18148706803

2、RS、IGNORECASE

RS有两种情况:
RS为单个字符:直接使用该字符作为分隔符进行分割记录
RS为多个字符:将其当做正则表达式,只要匹配正则表达式的符号,都用来做分割记录,设置预定义变量INGORECASE为非零值,表示正则忽略大小写

[root@lgh test]# echo "bababaABAB" |  awk 'BEGIN{IGNORECASE=1;RS="a+"}{print $0}' #正则忽略大小写切分读取
b
b
b
B
B
[root@lgh test]# echo "bababaABAB" | awk 'BEGIN{IGNORECASE=0;RS="a+"}{print $0}' #正则不忽略大小写切分读取
b
b
b
ABAB
[root@lgh test]# echo "bababaABAB" | awk 'BEGIN{IGNORECASE=1;RS="a"}{print $0}' #非正则,IGNORECASE无效
b
b
b
ABAB

3、分隔符FS、FIELDWIDTHS

  • FS为单个字符时,该字符即为字段分隔符
  • FS为多个字符时,则采用正则表达式模式作为字段分隔符
  • 特殊的,也是FS默认的情况,FS为单个空格时,将以连续的空白(空格、制表符、换行符)作为字段分隔符
  • 特殊的,FS为空字符串””时,将对每个字符都进行分隔,即每个字符都作为一个字段
  • 设置预定义变量IGNORECASE为非零值,正则匹配时表示忽略大小写(只影响正则,所以FS为单字时无影响)
[root@lgh test]# awk -F" +|@" '{print $1,$2,$3,$4,$5,$6,$7}' test.txt #正则,一个空格或者多个空格,或者@符进行分割
ID name gender age email phone
1 zhangs female 34 aaa 163.com 13423394013
2 lisi male 45 abb gmail.com 15608492523
3 wmzi female 61 ccc sohu.com 18848796505
4 wangw female 31 acc sohu.com 18848796506
5 liub male 56 gdd 163.com 18848796507
6 zhangf female 34 adb 139.com 18848796573
7 guany male 56 eab 189.com 17048796508
8 zhaoy female 35 189 163.com 17058796503
9 huangz male 65 dd 189.com 13648796593
10 lvbu male 43 byy sohu.com 18148796803
11 sunwk male 99 suk sohu.com 18148706803
[root@lgh test]# awk '{print $1,$2}' FS=":" /etc/passwd | head -2 #使用FS指定:为分割分
root x
bin x
[root@lgh test]# awk -F":" '{print $1,$2}' /etc/passwd | head -2 #使用-F指定:分隔符
root x
bin x

预定义变量FIELDWIDTHS按字符宽度分割字段
FIELDWIDTHS="1 2 3 4" :表示第一个字段1个字符,第二个2个字符,第三个3个字符,第四个4个字符

[root@lgh test]# cat name.txt #文件内容
ID name gender age email phone
1 female 34 aaa@163.com 13423394013
2 lisi male 45 abb@gmail.com 15608492523
[root@lgh test]# awk '{print $2}' name.txt #获取第二列,其中female是第三列的内容
name
female
lisi
[root@lgh test]# awk 'BEGIN{FIELDWIDTHS="8 8"}{print $2}' name.txt #正确的获取第二列
name lisi
[root@lgh test]# echo 'bababaABAB' | awk 'BEGIN{FIELDWIDTHS="1 2 3 4"}{print $1,$2,$3,$4}' #使用指定字符长度分割
b ab aba ABAB

4、FS、OFS

FS是读取行时的字段分割,OFS是输出时的字段分隔符

[root@lgh test]# awk 'BEGIN{OFS="="}{print $1,$2}' test.txt
ID=name
1=zhangs
2=lisi
3=wmzi
4=wangw
5=liub
6=zhangf
7=guany
8=zhaoy
9=huangz
10=lvbu
11=sunwk
[root@lgh test]# awk 'BEGIN{OFS="-"}{$1=$1;print $0}' test.txt #重组$0,修改$1,$2,...,$NF中的内容,以及增大或者减少NF的值,都会产生$0的重组
ID-name-gender-age-email-phone
1-zhangs-female-34-aaa@163.com-13423394013
2-lisi-male-45-abb@gmail.com-15608492523
3-wmzi-female-61-ccc@sohu.com-18848796505
4-wangw-female-31-acc@sohu.com-18848796506
5-liub-male-56-gdd@163.com-18848796507
6-zhangf-female-34-adb@139.com-18848796573
7-guany-male-56-eab@189.com-17048796508
8-zhaoy-female-35-189@163.com-17058796503
9-huangz-male-65-dd@189.com-13648796593
10-lvbu-male-43-byy@sohu.com-18148796803
11-sunwk-male-99-suk@sohu.com-18148706803

5、RS、ORS

RS是读取时的行分割,ORS是输出时的行分割

[root@lgh test]# echo -e "a\nb" |awk '{print $1}' #RS默认\n
a
b
[root@lgh test]# echo -e "a\nb" |awk 'BEGIN{ORS="hello word"}{print $1}' #使用hello word代替\n进行输出
ahello wordbhello word

6、OFMT

变量OFMT(Output format)定义的格式按照sprintf()相同的方式进行格式化。OFMT默认值为%.6g,表示有效位(整数部分加小数部分)最多为6。

[root@lgh test]# awk 'BEGIN{OFMT="%.3f";print 3.141592678}' #会进行四舍五入
3.142
[root@lgh test]# awk 'BEGIN{print 3.141592678}'
3.14159
常用格式符:
%c :将ASCII码转换为字符,
%d :转为整数
%e :科学计数方法输出
%o :转为8进制输出
%s :转为字符串
%x :转16进制

7、NR、FNR

NR是该程序读取的记录数,FNR是程序读取某个文件的的记录数

[root@lgh test]# awk '{print NR,FNR,$0}' test.txt test.txt #NR一直递增,FNR根据文件重新设置1,然后递增
1 1 ID name gender age email phone
2 2 1 zhangs female 34 aaa@163.com 13423394013
3 3 2 lisi male 45 abb@gmail.com 15608492523
4 4 3 wmzi female 61 ccc@sohu.com 18848796505
5 5 4 wangw female 31 acc@sohu.com 18848796506
6 6 5 liub male 56 gdd@163.com 18848796507
7 7 6 zhangf female 34 adb@139.com 18848796573
8 8 7 guany male 56 eab@189.com 17048796508
9 9 8 zhaoy female 35 189@163.com 17058796503
10 10 9 huangz male 65 dd@189.com 13648796593
11 11 10 lvbu male 43 byy@sohu.com 18148796803
12 12 11 sunwk male 99 suk@sohu.com 18148706803
13 1 ID name gender age email phone
14 2 1 zhangs female 34 aaa@163.com 13423394013
15 3 2 lisi male 45 abb@gmail.com 15608492523
16 4 3 wmzi female 61 ccc@sohu.com 18848796505
17 5 4 wangw female 31 acc@sohu.com 18848796506
18 6 5 liub male 56 gdd@163.com 18848796507
19 7 6 zhangf female 34 adb@139.com 18848796573
20 8 7 guany male 56 eab@189.com 17048796508
21 9 8 zhaoy female 35 189@163.com 17058796503
22 10 9 huangz male 65 dd@189.com 13648796593
23 11 10 lvbu male 43 byy@sohu.com 18148796803
24 12 11 sunwk male 99 suk@sohu.com 18148706803

8、ARGS、ARGV

预定义变量ARGV是一个数组,包含了所有的命令行参数。该数组使用从0开始的数值作为索引。
预定义变量ARGC初始时是ARGV数组的长度,即命令行参数的数量。
ARGV数组的数量和ARGC的值只有在awk刚开始运行的时候是保证相等的。

[root@lgh test]# awk 'BEGIN{print ARGC;for(i in ARGV){print "ARGV[" i "]= " ARGV[i]}}' a b c
4
ARGV[0]= awk
ARGV[1]= a
ARGV[2]= b
ARGV[3]= c

四、BEGIN和END

awk的所有代码(目前这么认为)都是写在语句块中的。
每个语句块前面可以有pattern,比如:pattern1{action1}pattern2{action3;action4;...}
语句块可分为3类:BEGIN语句块、END语句块和main语句块。其中BEGIN语句块和END语句块都是的格式分别为BEGIN{...}和END{...},而main语句块是一种统称,它的pattern部分没有固定格式,也可以省略,main代码块是在读取文件的每一行的时候都执行的代码块。

BEGIN代码块:
在读取文件之前执行,且执行一次
在BEGIN代码块中,无法使用$0或其它一些特殊变量 main代码块:
读取文件时循环执行,(默认情况)每读取一行,就执行一次main代码块
main代码块可有多个 END代码块:
在读取文件完成之后执行,且执行一次
有END代码块,必有要读取的数据(可以是标准输入)
END代码块中可以使用$0等一些特殊变量,只不过这些特殊变量保存的是最后一轮awk循环的数据
[root@lgh test]# awk 'BEGIN{print "hello word"}/^1/{print $0}END{print "END hello word"}' test.txt
hello word
1 zhangs female 34 aaa@163.com 13423394013
10 lvbu male 43 byy@sohu.com 18148796803
11 sunwk male 99 suk@sohu.com 18148706803
END hello word

实例如上:begin和end均只输出一次,main代码中匹配1开头的行并输出

五、运算符和逻辑运算符

5.1、运算符

$      # $(2+2)
++ --
^ **
+ - ! # 一元运算符
* / %
+ -
space # 这是字符连接操作 `12 " " 23` `12 " " -23`
| |&
< > <= >= != == # 注意>即是大于号,也是print/printf的重定向符号
~ !~
in
&&
||
?:
= += -= *= /= %= ^=

5.2、逻辑运算符

&&          逻辑与
|| 逻辑或
! 逻辑取反 expr1 && expr2 # 如果expr1为假,则不用计算expr2
expr1 || expr2 # 如果expr1为真,则不用计算expr2 # 注:
# 1. && ||会短路运算
# 2. !优先级高于&&和||
# 所以`! expr1 && expr2`等价于`(! expr1) && expr2`

六、输出和重定向

输出格式:print elem1,elem2,elem3...
逗号分隔要打印的字段列表,各字段都会自动转换成字符串格式,然后通过预定义变量OFS值(其默认值为空格)连接各字段进行输出
在print输出时会自动在尾部加上输出记录分隔符,输出记录分隔符的预定义变量为ORS,其默认值为\n
sprintf()采用和printf相同的方式格式化字符串,但是它不会输出格式化后的字符串,而是返回格式化后的字符串。所以,可以将格式化后的字符串赋值给某个变量。

[root@lgh test]# awk 'BEGIN{var=sprintf("%.3f",3.141592678);print var}' #使用sprintf进行格式化复制变量,然后输出
3.142
[root@lgh test]# awk 'BEGIN{var=sprintf("%d",3.141592678);print var}'
3
[root@lgh test]# awk 'BEGIN{OFMT="%.3f";print 3.141592678}' #使用OFMT指定格式进行输出
3.142
[root@lgh test]# awk 'BEGIN{OFMT="%d";print 3.141592678}'
3

重定向:

>filename时,如果文件不存在,则创建,如果文件存在则首先截断。之后再输出到该文件时将不再截断。
>>filename时,将追加数据,文件不存在时则创建。
awk中只要不close(),任何文件都只会在第一次使用时打开,之后都不会再重新打开。

[root@lgh test]# awk '{print $1,$2 > "name.txt"}' test.txt #重定向到name.txt文件
[root@lgh test]# cat name.txt
ID name
1 zhangs
2 lisi
3 wmzi
4 wangw
5 liub
6 zhangf
7 guany
8 zhaoy
9 huangz
10 lvbu
11 sunwk

七、数组

awk数组特性:

  • awk的数组是关联数组(即key/value方式的hash数据结构),索引下标可为数值(甚至是负数、小数等),也可为字符串
  • 在内部,awk数组的索引全都是字符串,即使是数值索引在使用时内部也会转换成字符串
  • awk的数组元素的顺序和元素插入时的顺序很可能是不相同的
  • awk数组支持数组的数组
  • 通过索引的方式访问数组中不存在的元素时,会返回空字符串,同时会创建这个元素并将其值设置为空字符串。

数组的访问和赋值都是通过制定下标就行赋值和访问,下标可以为整数,负数,字符串,小数
例如:

[root@lgh test]# cat a.awk
{
arr[i]="a"
arr[-1]="b"
print arr[i]
print arr[-1]
print length(arr)
print arr[1] #输出不存在的数组下标内容
print "################"
print length(arr) #长度+1
}
[root@lgh test]# echo 0 | awk -f a.awk
a
b
2 ################
3

数组遍历、删除、判读某key是否在arr中:

[root@lgh test]# cat a.awk
{
arr["x"]="a"
arr[-1]="b"
arr[4.5]=1
arr[4]=5
for(idx in arr){ #遍历
print arr[idx]
}
print "###############"
delete arr[4] #删除元素,也可以删除整个数组delete arr
for(idx in arr){ #遍历无序,与建立数组时输入的元素顺序不一定一致
print idx,arr[idx]
}
print "##############"
print (-1 in arr) #判断key=-1,是否在数组中,是返回1,不是返回0
print (99 in arr)
}
[root@lgh test]# echo 0 | awk -f a.awk
5
b
a
1
###############
-1 b
x a
4.5 1
##############
1
0

八、流程控制

8.1、判断、选择

if判断:

if(condition){statement }
if(condition){statement }else{statement }
if(condition){statement }else if(condition){statement }else{statement }

三目运算

expr1 ? expr2 : expr3

switch选择:

switch (expression) {
case value1|regex1 : statements1
case value2|regex2 : statements2
case value3|regex3 : statements3
...
[ default: statement ]
}
[root@lgh test]# awk 'NR>1{$1>5? var="B": var="A";print $1,var}' test.txt
1 A
2 A
3 A
4 A
5 A
6 B
7 B
8 B
9 B
10 B
11 B
[root@lgh test]# awk 'NR>1{if($1<5){print $1,"A"}else{print $1,"B"}}' test.txt
1 A
2 A
3 A
4 A
5 B
6 B
7 B
8 B
9 B
10 B
11 B

8.2、循环

while和do…while

while(condition){
statements
} do {
statements
} while(condition)

for循环

for (expr1; expr2; expr3) {
statement
} for (idx in array) {
statement
}
awk '{i=1;while(i<=NF){print $i;i++}}' test.txt
awk '{for(i=1;i<=NF;i++) print $i}' test.txt

8.3、关键字

break:break可退出for、while、do…while、switch语句。
continue:continue可让for、while、do…while进入下一轮循环。
next:next会在当前语句处立即停止后续操作,并读取下一行,进入循环顶部。
nextfile:nextfile会在当前语句处立即停止后续操作,并直接读取下一个文件,并进入循环顶部。
exit:直接退出awk程序,注意,END语句块也是exit操作的一部分,所以在BEGIN或main段中执行exit操作,也会执行END语句块。如果真的想直接退出整个awk,则可以先设置一个flag变量,然后在END语句块的开头检查这个变量再exit。

[root@lgh test]# awk 'NR==3{next}{print  $0}' test.txt  #匹配到了第3行,然后跳过
ID name gender age email phone
1 zhangs female 34 aaa@163.com 13423394013
3 wmzi female 61 ccc@sohu.com 18848796505
4 wangw female 31 acc@sohu.com 18848796506
5 liub male 56 gdd@163.com 18848796507
6 zhangf female 34 adb@139.com 18848796573
7 guany male 56 eab@189.com 17048796508
8 zhaoy female 35 189@163.com 17058796503
9 huangz male 65 dd@189.com 13648796593
10 lvbu male 43 byy@sohu.com 18148796803
11 sunwk male 99 suk@sohu.com 18148706803
[root@lgh test]# awk 'NR==3{nextfile}{print $0}' test.txt test.txt #匹配到第3行,跳过该文件,执行下一个文件
ID name gender age email phone
1 zhangs female 34 aaa@163.com 13423394013
ID name gender age email phone
1 zhangs female 34 aaa@163.com 13423394013
2 lisi male 45 abb@gmail.com 15608492523
3 wmzi female 61 ccc@sohu.com 18848796505
4 wangw female 31 acc@sohu.com 18848796506
5 liub male 56 gdd@163.com 18848796507
6 zhangf female 34 adb@139.com 18848796573
7 guany male 56 eab@189.com 17048796508
8 zhaoy female 35 189@163.com 17058796503
9 huangz male 65 dd@189.com 13648796593
10 lvbu male 43 byy@sohu.com 18148796803
11 sunwk male 99 suk@sohu.com 18148706803
[root@lgh test]# awk 'BEGIN{ print "hello work";exit}END{print "god job"}'  #默认会执行END中的代码
hello work
god job
[root@lgh test]# awk 'BEGIN{ print "hello work";;exit flag=1}END{if(flag) exit ;print "god job"}' #使用flag跳出
hello work

九、变量

awk中语句块没有作用域,都是全局变量(除非是函数中的形参)

变量的赋值:

可以x=y=z=5,等价于z=5 y=5 x=5
可以将赋值语句放在任意允许使用表达式的地方
x != (y = 1)
awk 'BEGIN{print (a=4);print a}'

awk中声明变量的位置:

在BEGIN或main或END代码段中直接引用或赋值
使用-v var=val选项,可定义多个,必须放在awk代码的前面
它的变量声明早于BEGIN块
普通变量:awk -v age=123 'BEGIN{print age}'
使用shell变量赋值:awk -v age=$age 'BEGIN{print age}' 在awk代码后面使用var=val参数
它的变量声明在BEGIN之后
awk '{print n}' n=3 a.txt n=4 b.txt
awk '{print $1}' FS=' ' a.txt FS=":" /etc/passwd
使用Shell变量赋值:awk '{print age}' age=$age a.txt

十、函数和自定义函数

10.1、自定义函数

使用function关键字来定义函数:

function func_name([parameters]){
function_body
}

函数可以定义的位置:

awk '_ BEGIN{} _ MAIN{} _ END{} _'  #可以定义在任何下划线的地方
[root@lgh test]# cat a.awk
BEGIN{
f()
}
function f(){
print
"自定义函数"
}

{}#main
END{}
[root@lgh test]# echo 0 | awk -f a.awk
自定义函数
[root@lgh test]# vim a.awk
[root@lgh test]# cat a.awk
function f(){
print
"自定义函数"
}
BEGIN{
f()
}
{}#main
END{}
[root@lgh test]# echo 0 | awk -f a.awk
自定义函数
[root@lgh test]# vim a.awk
[root@lgh test]# cat a.awk
BEGIN{
f()
}
{}#main
END{}
function f(){
print
"自定义函数"
}

[root@lgh test]# echo 0 | awk -f a.awk
自定义函数

带参数和return的自定义函数:

[root@lgh test]# cat a.awk
BEGIN{
print f(1,2)
}
{}#main
END{}
function f(a,b){
print "自定义函数"
return a+b
} [root@lgh test]# echo 0 | awk -f a.awk
自定义函数
3

参数的传递形式:

传递普通变量时,是按值拷贝传递

  • 直接拷贝普通变量的值到函数中
  • 函数内部修改不会影响到外部

传递数组时,是按引用传递

  • 函数内部修改会影响到外部

awk变量作用域:

  • awk只有在函数参数中才是局部变量,其它地方定义的变量均为全局变量。
  • 函数内部新增的变量是全局变量,会影响到全局,所以在函数退出后仍然能访问
  • 函数参数会遮掩全局同名变量,所以在函数执行时,无法访问到或操作与参数同名的全局变量,函数退出时会自动撤掉遮掩,这时才能访问全局变量。所以,参数具有局部效果。
[root@lgh test]# cat a.awk
BEGIN{
a=50 #全局
print a #50
print f(a,2) #42
print a #50
}
{}#main
END{}
function f(a,b){
print "自定义函数"
a=40 #覆盖了全局变量a
print "局部",a
return a+b #40+2
} [root@lgh test]# echo 0 | awk -f a.awk
50
自定义函数
局部 40
42
50

10.2、getline函数

getline函数用于从文件、标准输入或管道中读取数据,并按情况设置变量的值。getline可以自动不断的加载下一行。如果能读取记录,则getline的返回值为1,遇到输入流的尾部时,返回值为0,不能读取记录(如文件没有读取权限、文件不存在)时,返回值为“-1"。

其中:

  1. getline:会从主输入文件中读取记录。会同时设置$0,NF,NR,FNR。
  2. getline var:会从主输入文件中读取记录,并将读取的记录赋值给变量var。会同时设置var,NR,FNR。
  3. getline <file:从外部文件file中读取记录。同时会设置$0,NF。
  4. getline var <file:从外部文件file中读取记录,并将读取的记录赋值给变量var。会同时设置var。
  5. cmd | getline:从管道中读取记录。会同时设置$0,NF。
  6. cmd | getline var:从管道中读取记录,并将读取的记录赋值给变量var。会同时设置var。

getline与netx的区别
getline:读取下一行之后,继续执行getline后面的代码
next:读取下一行,立即回头awk循环的头部,不会再执行next后面的代码

[root@lgh test]# awk '/^1/{print;getline;print}' test.txt #匹配1开头的行,在1开头的时候,获取到了下一行,也打印了出来
1 zhangs female 34 aaa@163.com 13423394013
2 lisi male 45 abb@gmail.com 15608492523
10 lvbu male 43 byy@sohu.com 18148796803
11 sunwk male 99 suk@sohu.com 18148706803
[root@lgh test]# awk '/^1/{print;getline;print;exit}' test.txt #执行了getline,print,然后exit了
1 zhangs female 34 aaa@163.com 13423394013
2 lisi male 45 abb@gmail.com 15608492523
[root@lgh test]# awk '/^1/{print;if((getline var)<0)exit;print var}' test.txt #getline赋值给了var
1 zhangs female 34 aaa@163.com 13423394013
2 lisi male 45 abb@gmail.com 15608492523
10 lvbu male 43 byy@sohu.com 18148796803
11 sunwk male 99 suk@sohu.com 18148706803
[root@lgh test]# awk '/^1/{print;if((getline var)<0)exit;print $0}' test.txt #getline赋值给了var,但是没有设置$0
1 zhangs female 34 aaa@163.com 13423394013
1 zhangs female 34 aaa@163.com 13423394013
10 lvbu male 43 byy@sohu.com 18148796803
10 lvbu male 43 byy@sohu.com 18148796803
[root@lgh test]# awk '/^2/{while((getline var <"a.awk")>0)print var;close("a.awk")}' test.txt #匹配2开头的行,只有一行,所以后面只执行一次,把a.awk里面的内容赋值给var变量,然后输出
BEGIN{
a=50
print a
print f(a,2)
print a
}
{}#main
END{}
function f(a,b){
print "自定义函数"
a=40
print "局部",a
return a+b
} [root@lgh test]# awk '/^2/{while((getline<"a.awk")>0){print};close("a.awk")}' test.txt #匹配2开头的行,只有一行,所以只执行一次,输出a.awk中的内容
BEGIN{
a=50
print a
print f(a,2)
print a
}
{}#main
END{}
function f(a,b){
print "自定义函数"
a=40
print "局部",a
return a+b
}
[root@lgh test]# awk '/^2/{"date"|getline;print}' test.txt #使用linux的date命令,通过管道,使用getline读取
Tue Nov 24 19:44:28 CST 2020
[root@lgh test]# awk '/^2/{"date"|getline var ;print var}' test.txt
Tue Nov 24 19:44:41 CST 2020
[root@lgh test]# awk '/^1/{"date"|getline var ;print var}' test.txt
Tue Nov 24 19:44:48 CST 2020
Tue Nov 24 19:44:48 CST 2020
Tue Nov 24 19:44:48 CST 2020

awk虽然强大,但是有些数据仍然不方便处理,这时可将数据交给Shell命令去帮助处理,然后再从Shell命令的执行结果中取回处理后的数据继续awk处理。awk通过|&符号来支持coproc。

awk_print[f] "something" |& Shell_Cmd
Shell_Cmd |& getline [var]

这表示awk通过print输出的数据将传递给Shell的命令Shell_Cmd去执行,然后awk再从Shell_Cmd的执行结果中取回Shell_Cmd产生的数据

[root@lgh test]# cat a.awk
BEGIN{
CMD="sed -nr \"s/.*@(.*)$/\\1/p\""; #获取邮箱@符号后面的内容
}
NR>1{
print $5; #输出邮箱名
print $5 |& CMD; #截取邮箱@符号后面的内容
close(CMD,"to");
CMD |& getline email_domain; #把邮箱@后面的内容赋值给变量email_domain
close(CMD);
print email_domain; #输出email_domain }
[root@lgh test]# awk -f a.awk test.txt
aaa@163.com
163.com
abb@gmail.com
gmail.com
ccc@sohu.com
sohu.com
acc@sohu.com
sohu.com
gdd@163.com
163.com
adb@139.com
139.com
eab@189.com
189.com
189@163.com
163.com
dd@189.com
189.com
byy@sohu.com
sohu.com
suk@sohu.com
sohu.com

10.3、内置字符串函数

index(str1,str2):返回子串str2在字符串str1中第一次出现的位置。如果没有指定str1,则返回0。
length(str1):返回字符串str1的长度。如果未给定str1,则表示计算"$0"的长度。
substr(str1,p):返回str1中从p位置开始的后缀字符串。
substr(str1,p,n):返回str1中从p位置开始,长度为n的子串。
match(str1,regexp):如果regexp能匹配str1,则返回匹配起始位置。否则返回0。它会设置内置变量RSTART和RLENGTH的值。
split(str1,array,sep):使用字段分隔符sep将str1分割到数组array中,并返回数组的元素个数。如果未指定sep则采用FS的值。因此该函数用于切分字段到数组中,下标从1开始。
sprintf(fmt,expr):根据printf的格式fmt,返回格式化后的expr。
sub(regexp,rep,str2):将str2中第一个被regexp匹配的字符串替换成rep,替换成功则返回1(表示替换了1次),否则返回0。注意是贪婪匹配。
sub(regexp,rep):将"$0"中第一个被regexp匹配的字符串替换成rep,替换成功则返回1,否则返回0。注意是贪婪匹配。
gsub(regexp,rep,str2):将str2中所有被regexp匹配的内容替换成rep,并返回替换的次数。
gsub(regexp,rep):将"$0"中所有被regexp匹配的内容替换成rep,并返回替换的次数。
toupper(str):将str转换成大写字母,并返回新串。
tolower(str):将str转换成小写字母,并返回新串。

10.4、内置数值函数

int(expr)     截断为整数:int(123.45)和int("123abc")都返回123,int("a123")返回0
sqrt(expr) 返回平方根
rand() 返回[0,1)之间的随机数,默认使用srand(1)作为种子值
srand([expr]) 设置rand()种子值,省略参数时将取当前时间的epoch值(精确到秒的epoch)作为种子值

参考:

https://www.cnblogs.com/f-ck-need-u/p/7509812.html#9-awk-

https://www.cnblogs.com/f-ck-need-u/p/12688355.html

https://www.cnblogs.com/hdk1993/p/4637525.html

最新文章

  1. 把你的Project发布到GitHub上
  2. 常用算法&mdash;&mdash;排序(三)
  3. UVa 445 - Marvelous Mazes
  4. C++ Primer学习笔记二
  5. ckeditor异常问题
  6. cocos2d-x 扩充引擎基类功能 引起的头文件重复包含问题的分析
  7. Java基础知识强化63:Arrays工具类之方法源码解析
  8. 如何写一个网页标题title的闪动提示
  9. Linux下编译安装qemu和libvirt
  10. ThinkPhp学习01
  11. Java 算法
  12. WebService--jax
  13. JS 循环定时的一些思考
  14. Django 在Django项目里单独运行某个py文件
  15. BZOJ.1178.[APIO2009]会议中心(贪心 倍增)
  16. 22状态模式State
  17. struts2+dojo实现datagrid
  18. 廖雪峰Java2面向对象编程-6Java核心类-5枚举类
  19. 写一个限制上传文件大小和格式的jQuery插件
  20. JVM 系列 ClassLoader

热门文章

  1. 腾讯云函数免费搭建onedrive网盘
  2. ubuntu 16.04 Chrome
  3. gethub网址链接
  4. 实用!8个 chrome插件玩转GitHub,单个文件下载小意思
  5. git的远程分支是干啥的,和本地的有什么区别?
  6. DevOps 视角的前后端分离与实战
  7. python机器学习实现人脸图片自动补全
  8. JavaWeb中的关于html、jsp、servlet下的路径问题
  9. openssl ec/ecparam/errstr/ripemd160/camellia-128-ecb/camellia-192-cbc/camellia-192-ecb3条指令及1个哈希算法3个加密算法的学习
  10. Spring AOP实现注解式的Mybatis多数据源切换