NAME

gawk - pattern scanning and processing language

模式扫描和处理语言

awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。

格式

gawk [options] 'program' FILE ...
program: PATTERN{ACTION STATEMENTS}

语句之间用分号分隔

常用选项

  • -F'' :指明输入时用到的字段分隔符,从文件中读取数据时,用什么做分隔符,和内建变量FS一个意思
  • -v var=value :自定义变量
  • -f /path/to/awk_script :将awk调用文件里写好的awk语法进行执行。

表达式

表达式是最简单的语句,大多数其他语句都是由不同类型的表达式组合而成。初等表达式与其他表达式通过运算符组合在一起,形成一个新的表达式。初等表达式是最原始的构造块:它们包括常量、变量、数组引用、函数调用、以及各种内建变量,例如字段的名字。

常量

awk中只有两种常量:字符串和数值,将一个字符序列用一对双引号包围起琰就创建一个字符串常量。所有的数都用浮点格式存储。

变量

用户定义的,内建的,或字段。用户定义的变量名字由数字,字母与下划线构成,但是名字不能以数字开始。所有的内建变量的名字都是大写字母。

每一个变量都有一个值,这个值可以是字符串或数值,或两者都是。因为变量的类型不需要事先声明,所以awk需要根据上下文环境推断出变量的类型。当需要时,awk可以把字符串转化为数值,或反之。

内建变量

变量 意义 默认值
ARGC 命令行参数的个数 -
ARGV 命令行参数数组 -
FILENAME 当前输入文件名 -
FNR 当前输入行的个数 -
FS 控制着输入行的字段分割符 “ ”
NF 记录每行的字段个数 -
NR 到目前为止读到的行的数量 -
OFS 输出字段分割符 “ ”
ORS 输出时的换行符 "\n"
RLENGTH 被函数match匹配的字符串的长度 -
RS 输入时的换行符 "\n"
RSTART 被函数match匹配的字符串的开始
SUBSEP 下标分割符 "\034"

**NF** (number of field)
有时候,必须总是通过\$1,\$2这样的形式引用字段,但是任何表达式都可以出现在\$后面,用来指明一个字段的编号:表达式被求值,求出的值被当作字段的编号。awk计算当前输入行的字段数量,并将它存储在一个内奸的变量中,这个变量叫作`NF`,因此
`{print NF,$1,$NF}`
将会打印:每一行的字段数量,第一个字段,以及最后一个字段
```
[root@node1 ~]# echo "dm ft 12" | awk '{print NF,$1,$NF}'
3 dm 12
[root@node1 ~]# echo "dm ft 12" | awk '{print NF,$1,$NF-1}'
3 dm 11
[root@node1 ~]# echo "dm ft 12" | awk '{print NF,$1,$(NF-1)}'
3 dm ft
```

**NR** (number of record)
`NR`这个变量计算到目前为止,读取到的行的数量。
```
[root@node1 ~]# echo -e "dm ft\nft dm" | awk '{print NR,$0}'
1 dm ft
2 ft dm
```

**FNR**(file record number)
`FNR`表示从当前输入的行数,总共读取的行数。分别在与`NR`的地方是`FNR`对各文件分别计数
```
[root@node2007 ~]# awk -F':' '{print FNR}' /etc/passwd /etc/shadow
1
2
...
20
1
2
...20
[root@node2007 ~]# awk -F':' '{print NR}' /etc/passwd /etc/shadow
1
2
...
40

[root@node1 ~]# awk 'FNR == 1' /etc/passwd

root❌0:0:root:/root:/bin/bash


<br />
**FILENAME**
`FILENAME`表示当前输入文件名

[root@node2007 tmp]# awk 'FILENAME == "/tmp/a.log" {print }' /tmp/*.log

hello

[root@node2007 tmp]# awk '{print FILENAME}' /tmp/a.log /tmp/b.log

/tmp/a.log

/tmp/b.log


<br />
**FS**(input field seperator)
输入字段分割符,默认为空白字符。

[root@node2007 ~]# echo "root:x" | awk -v FS=':' '{print $1}'

root

[root@node2007 ~]# echo "root:x" | awk -F':' '{print $1}'

root

<br />
**OFS**(output field seperator)
输出字段分隔符,默认为空白字符。

[root@node2007 ~]# echo "root:x" | awk -v FS=':' -v OFS="|" '{print $1,$2}'

root|x

<br />
**RS**(input record seperator)
输入时的换行符。默认`\n`

[root@node2007 ~]# echo "Hello World" | awk -v RS=' ' '{print}'

Hello

World

[root@node2007 ~]# echo "Hello World" | awk -v RS='o' '{print}'

Hell

W

rld

<br />
**ORS**(output record seperator)
输出时的换行符。默认`\n`

[root@node2007 ~]# echo "Hello World" | awk -v ORS='\t' '{print}'

Hello World [root@node2007 ~]# echo "Hello World" | awk -v ORS='#' '{print}'

Hello World#[root@node2007 ~]#

<br />
**ARGC**
命令行参数的个数。awk命令本身是第一个参数,也就是数组的零下标,之后的常用选项不算做参数,最后提供的参数也是参数。 **ARGV**
数组,保存的是命令行所给定的各参数 ####内建函数
内建函数分为`算术函数`和`字符串函数`个人使用算术函数不多,这里只讲`rand`算术函数,剩余都是字符串函数。 **算术函数:**
**rand()**
返回0和1之间的一个随机数

[root@node2007 ~]# echo | awk '{print rand()*10}' #返回0-10之间的数字

2.37788

<br />
**字符串函数:** 函数|描述
-|-
index(s,t)|返回字符串t在s中第一次出现的位置,如果t没有出现的话,返回0
length(s)|返回s包含的字符个数
split(s,a)|用`FS`将s分割到数组a中,返回字段的个数
split(s,a,fs)|用`fs`分割s到数组a中,返回字段的个数
sub(r,s)|将\$0的最左最长的,能被r匹配的子字符串替换为s,返回替换发生的次数
sub(r,s,t)|t就是选定区域,然后执行`sbu(r,s)`
substr(s,p)|返回s中从位置p开始的后缀
substr(s,p,n)|返回s中从位置p开始的,长度为n的子字符串
gsub(r,s)|将\$0中所有出现的r替换为s,返回替换发生的次数
gsub(r,s,t)|将字符串t中所有出现r替换为s,返回替换发生的次数 <br /> **index**

[root@node2007 ~]# echo -e "hello\nworld"| awk '{print index($1,"o")}'

5

2

[root@node2007 ~]# echo -e "hello\nworld"| awk '{print index($1,"a")}'

0

0

<br />
**length**

[root@node2007 ~]# echo -e "hello\nworldd"| awk '{print length($1)}'

5

6

<br />
**split**

[root@node2007 ~]# echo -e "/etc/nginx/nginx.conf"| awk '{split($0,a,"/");for(i=1;i<=length(a);i++){if (a[i] == ""){countine}else{print a[i]}}}' #语法格式后面会详细介绍

etc

nginx

nginx.conf

<br />
**sub,gsub**
`sub`和`gsub`相当于`sed`命令替换命令后带`g`参数的效果。
匹配指定域/记录中最大、最靠左边的子字符串的正则表达式,并用替换字符串替换这些字符串。如果没有指定目标字符串就默认使用整个记录。替换只发生在第一次匹配的时候。

[root@node2007 ~]# echo -e "hello"| awk '{print $0,sub("l","L"),$0}'

hello 1 heLlo

[root@node2007 ~]# echo -e "hello world"| awk '{print $0,sub(/l+/,"L",$2),$0}'

hello world 1 hello worLd

[root@node2007 ~]# echo -e "hello world"| awk '{gsub(/l+/,"L");print}'

heLo worLd

[root@node2007 ~]# echo -e "hello world"| awk '{gsub(/l+/,"L",$2);print}'

hello worLd


<br />
**substr**

[root@node2007 ~]# echo -e "/etc/nginx/nginx.conf"| awk -v FS='/' '{print substr($NF,1)}' #给定一个位置,然后将后缀输出

nginx.conf

[root@node2007 ~]# echo -e "/etc/nginx/nginx.conf"| awk -v FS='/' '{print substr($NF,1,5)}' #限定输出长度

nginx

<br />

####字段变量
当前输入行的字段从\$1,\$2,一直到\$NF;\$0表示整行。字段变量与其他变量相比没什么不同,也可以用在算术或字符串运算中,也可以被赋值。

[root@node2007 ~]# echo "hello" | awk '{$1 = "world";print}'

world

[root@node2007 ~]# echo "10" | awk '{$1 = $1 / 2;print $1}'

5

<br />

###PATTERN(模式)
####BEGIN&END
`BEGIN`与`END`这两个模式不匹配任何输入行,实际情况是,当awk从输入读取数据之前,`BEGIN`的语句开始执行;当所有输入数据被读取完毕,`END`语句开始执行。于是,`BEGIN`和`END`分别提供了一种控制初始化与扫尾的方式。
`BEGIN`的一个常用用途是更改输入行被分割为字段的默认方式。使用内键变量`FS`和常用选项`-F`

[root@node1 tmp]# cat countries

USSR 8649 275 Asia

Canada 3852 25 North America

China 3705 1032 Asia

USA 3615 237 North America

[root@node1 tmp]# awk 'BEGIN{FS="\t"

printf("%10s%6s%5s %s\n\n",

"country","area","pop","continent")

}

{ printf("%10s %6d %5d %s\n",$1,$2,$3,$4)

area = area + $2

pop = pop + $3

}

END{printf("\n%10s %6d %5d\n","TOTAL",area,pop)}' countries

country area pop continent

  USSR   8649   275    Asia
Canada 3852 25 North America
China 3705 1032 Asia
USA 3615 237 North America TOTAL 19821 1569
<br />
####relational expression
`relational expression{action}`表示每碰到一个使`relational expression`为真的输入行,`{action}`就执行。为真:指的是其值非零或非空。这里的`relational expression`其实就是使用操作符来做判断,并根据判断结果来确定是否要执行`{action}`。

[root@node2007 tmp]# awk -F':' '$3 == 0{print}' /etc/passwd

root❌0:0:root:/root:/bin/bash


* `relational expression`是表达式:
- 比较操作符:`>,<,>=,<=,!=,==,~(匹配),!~(不匹配)`
- 算术操作符:`+,-,*,/,^(指数运算),%`
- 赋值操作符:`=,+=,-=,/=,%=,^=,++,--`
- 模式匹配符:`||,&&,!(使用时最好将所需取反用小括号括起来)` <br />
####/regular expression/
`/regular expression/{action}`仅处理能够被此处模式匹配到的行。此处的`regular`即可使用`regex`(正则表达式)来做匹配

[root@node2007 tmp]# awk -F':' '$1 ~ /oot>/{print}' /etc/passwd #只要$1包含oot结尾的单词即为真

root❌0:0:root:/root:/bin/bash

[root@node2007 tmp]# awk -F':' '$1 ~ /o*t>/{print $1}' /etc/passwd #这里的的o*匹配的是零个o或者任意个o,与glob语法中的*请区分下

root

halt

<br />

####line ranges
`/part1/,/part2/`匹配一个或多个输入行,这些输入行从匹配part1的行开始,到匹配part2的行结束,包括这两行;part1可以与part2匹配到同一行。`part`匹配也可使用正则表达式。
**注:不支持直接给出数字,但可以使用内键变量`FNR`来代替直接给出数字。**

[root@node2007 tmp]# echo -e "1\n2\n1\n3\n2\n3" | awk '/1/,/3/{print}' #这里可以看出只匹配第一个

1

2

1

3

[root@node2007 tmp]# echo -e "1\n2\n1\n3\n2\n3" | awk 'FNR == 3 {print}'

1

[root@node2007 tmp]# echo -e "1\n2\n1\n3\n2\n3" | awk 'FNR <= 3 {print}'

1

2

1

<br />
####模式总结: 模式|例子|匹配
-|-|-
BEGIN|BEGIN|输入被读取之前
END|END|所有输入被读取完之后
expression|$3 < 100|第3个字段小于100的行
string-matching| $2 ~ /Asia/|第2字段含有Asia的行
compound|\$3 < 100 && \$2 ~ /Asia/|第3个字段小于100并且第2字段含有Asia的行
range|NR==10,/^root\>/|第10行到行首第一个单词是root的之间的行。 **额外使用技巧:**
正则表达式可以不用包围在两个斜杠中,可以将正则表达式赋值给一个变量,然后使用该变量匹配数据。

BEGIN { digits = "[1]+$" }

$2 ~ digits



<br />

###流程控制语句
> awk提供有用于决策`if-else`语句,以及循环语句,它们只能用在动作(action)里。所有的这些都来源于C语言,如果你熟悉C语言,我相信下面的语法对你来说小菜一碟。
> awk 提供花括号用于语句组合,`if-else`用于决策,`while`,`for`,`do`语句用于循环。一条单独的语句总是可以被替换为一个被花括号包围起来的语句列表,列表中的语句用换行符或分号分开,换行符可以出现在任何左花括号之后,也可心出现在任何右花括号之前。

流程控制语句:

if (expression) statements

if (expression) statements1 else statements2

while (expression) statements

for (variable in array) statements

do statements while (expression) #执行statements,如果为expression为真就重复

状态控制

break #退出循环

continue #退出当前循环

next #开始输入主循环的下一次迭代,BEGIN后算主循环

exit #执行END动作;如果已经在END动作内,那就退出程序,将expression作为程序退出状态返回

<br />
####if
语法格式:

PATTERN {

if (condition)

{

action

....

}

else

action

}

if-else可以缩写成如下格式:

selector?if-true-expression:if-false-expression

#if-else-if

PATTERN {

if (condition1)

{

action1

}

else if (condition2)

{

action2

}

...

else

action

}


示例:

[root@node2007 ~]# echo "1 2 3" | awk '{if ($1 == 1){print "\$1 equal 1"}}'

$1 equal 1

[root@node2007 ~]# echo "1 2 3" | awk '{if ($1 != 1){print "\$1 equal 1"}else{print "other is 2 3"}}'

other is 2 3

[root@node2007 ~]# echo "1 2 3" | awk '{if ($1 != 1){print "\$1 equal 1"}else if ($2 == 2){print "\$2 equal 2"}}'

$2 equal 2


<br />
####for
C语言的for语句,这里不做解释。

PATTERN {

for (i=1;i<=10;++i){

action

...

}

}

无限循环

PATTERN {

for (;

最新文章

  1. kvm 使用入门详解
  2. 【原】你真的懂iOS的autorelease吗?
  3. Object-C中ARC forbids explicit message send of &#39; &#39; 错误
  4. Greedy:Stripes(POJ 1826)
  5. IO流--流转换
  6. TFS 2010 配置的时候,提示TF255466错误
  7. String 内在分配解析
  8. c语言面试题之sizeof
  9. Unity 单元测试(NUnit,UnityTestTools)
  10. Golang性能调优入门
  11. 【转】关于ios10中ATS的问题
  12. 异步方式向WPF ListBox控件中一条一条添加记录
  13. DropDownList如何绑定DataTable,如何绑定DataSet
  14. centos下 redmind2.6安装
  15. COCOMOII的使用说明
  16. 手把手教你如何优雅的使用Aop记录带参数的复杂Web接口日志
  17. (90)Wangdao.com第二十三天_JavaScript CSS 操作
  18. Linux shell 菜鸟学习笔记....
  19. SharePoint 2013自定义Providers在基于表单的身份验证(Forms-Based-Authentication)中的应用
  20. 九度OJ-1131-合唱排队-双向递增子序列

热门文章

  1. python 文件读写模式区别,以及如何边写入边保存flush()
  2. ssl证书转换cer转pem
  3. 不用FTP,直接Windows与Linux下互传文件
  4. Azure容器监控部署(下)
  5. Postman接口测试【3】_自动添加随笔
  6. losetup命令使用
  7. 【LeetCode】缺失的第一个正数【原地HashMap】
  8. c++中如何判断sqlite表是否存在
  9. 一个萝卜一个坑#我的C坑_两局变量
  10. k8s-Namespace(命名空间)