跳转至

awk 示例


2016-02-17 by dongnan

输入驱动

awk是输入驱动的:

file a.txt b.txt
a.txt: ASCII text
b.txt: empty

文件有1行内容则输出1行

awk '{print}' a.txt
print this line

空文件没有输出

awk '{print}' b.txt
# 什么都没有输出

程序设计模型

awk 由主输入(mian input)循环组成,主输入循环执行次数由输入的行数决定。

awk 允许编写两个特殊的过程,BEGINEND 分别对应主输入循环执行前和主输入循环执行后,BEGINEND过程是可选的:

# 计算第一列的总和
find  -name "*.gz" -exec du {} \; | awk 'BEGIN{sum=0}{sum+=$1}END{print sum/1024" MB"}'

awk 由3部分组成,处理前,处理中,处理后。

在主输入循环中,指令被写成一些列的模式/动作过程,模式是用于匹配入行的规则,以确定动作是否应用这些输入行:

# 语法
awk '/pattern/ {action}'

模式匹配

当 awk 读入一行时,将匹配每个模式的规则,只有与一个特定的模式相匹配的输入行才能成为操作对象,如果没有指定操作,将打印匹配输入的行。

awk '/^root/' /etc/passwd
root:x:0:0:root:/root:/bin/bash

匹配空行

echo "" | awk '/^$/ {print "this is blank line"}'
this is blank line

匹配数字

echo "123" | awk '/[0-9]+/ {print "this is an integer"}'
this is an integer

匹配字符

echo "abc" | awk '/[A-Za-z]+/ {print "this is a string"}'
this is a string

注释

awk 注释以字符 # 开始,以换行符结束:

echo "123" | awk '/[0-9]/;#这里是注释'
123

记录和字段

awk 假设输入是有结构的,最简单的是将空格或者制表符分隔的单词作为字段,连续的两个或者多个空格或制表符会被当做一个分隔符。

字段引用

awk 使用$符号来操作字段,例如$1 表示第1个字段,$2表示第2个字段等等,$0表示整个输入行(记录)。 print 语句使用,逗号分隔参数,逗号输出的分隔符为空格。

awk -F: '{print $1,$2}' /etc/passwd | head -n2
root x
bin x

可以使用任何计算值为整数的表达式来表示一个字段

awk -F: '{print $(NF-2)}' /etc/passwd | head -n2
root

daemon

以解析nginx日志为例,日志内容如下:

tail -n1 access.log
150.138.216.81 - - [10/Dec/2018:14:45:52 +0800] "POST /works/MemberLogin.asp HTTP/1.1" 404 95 "-" 
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; .NET CLR 1.1.4322)" 101.227.1.202 0.002

最后一列

tail -n1 access.log | awk '/"Mozilla\/4.0 \(compatible;/ {print $NF}'
0.002

倒数第2列

tail -n1 access.log | awk '/"Mozilla\/4.0 \(compatible;/ {print $(NF-1)}'
101.227.1.203

依次类推,倒数第3列

tail -n1 access.log | awk '/"Mozilla\/4.0 \(compatible;/ {print $(NF-2)}'
1.1.4322)"

分隔符

命令行下 -F 选项,用于改变分隔符,默认的分隔符是空格。通过定义系统变量FS可以改变字段分隔符,变量FS需要提前在BEGIN过程定义。

awk 'BEGIN{FS=":"} {print $(NF-2)}' /etc/passwd | head -n2
root
daemon

也可以使用正则表达式,指定几个字符作为分隔符。

awk 'BEGIN{FS="[,:\t]"} {print $(NF-2)}' /etc/passwd | head -n2
root
daemon

匹配字段

awk -F: '/bash/ {print $1,$NF}' /etc/passwd
root /bin/bash
dongnan /bin/bash

#~ 运算符,用于匹配正则表达式
awk -F: '$1 ~ /root/ {print $1,$NF}' /etc/passwd
root /bin/bash

#! 运算符,表达式值取反
awk -F: '$1 !~ /root/ {print $1,$NF}' /etc/passwd |head -n1
daemon /usr/sbin/nologin

表达式

表达式由数字和字符串常量,变量,操作符,函数,正则表达式组成。

匹配大于1秒的mysql慢查询记录;

awk '/Query_time/ && $3 > 1' slow_queries.log

# Query_time: 1.007705  Lock_time: 0.000033 Rows_sent: 769  Rows_examined: 769
# Query_time: 12.118960  Lock_time: 0.000248 Rows_sent: 0  Rows_examined: 0
# Query_time: 1.253649  Lock_time: 0.000043 Rows_sent: 793  Rows_examined: 793
  • '这里都是表达式'
  • /匹配的正则或字符串/ -&& 逻辑与操作符
  • $3 > 1 表达式

常量

有两种类型:字符串型和数字型("abc"123),字符串必须由引号括起来;

变量

变量是引用值的标识符,定义变量只需要为它定义一个名字并将数据赋值给它即可,变量区分大小写变量不必初始化,awk 自动初始化空字符串或数字0

等号 = 是赋值操作符,空格是连接字符串的操作符

echo "" | awk '{x=1;y="hello" "world";print y,x}'

helloworld 1

关系表达式

关系操作符和布尔操作符用于在两个表达式之间进行比较;

关系运算符

<  #小于
<= #小于等于
>  #大于
>= #大于等于
!= #不等的
== #相等的
~  #匹配
!~ #不匹配

关系表达式可用在模式中来控制特殊的操作

$1==month {print $NF}

正则表达式经常用斜杠包围,这经常被作为正则表达式常量

/Failed/ { ++F[$(NF-3)]}

然而也不限于正则表达式常量,当使用关系操作符时,右边的表达式可以是awk 中的任意表达式。awk 将他作为一个字符串处理

$6 ~ /Failed/ {print $(NF-3)}

布尔运算符

||  #逻辑或   
&&  #逻辑与        
!   #逻辑非

可以使用圆括号来改变优先级

($1=="Jan" && $2==19) && /Failed/ {++F[$(NF-3)]}

!操作符 结合 in操作符非常有用,可用来判断某个下标是否在数组中

!(i in F)

系统变量

awk 中有许多系统变量或内置变量。

系统变量FS定义字段分隔符,它的默认值为一个空格,FS变量可以被设置为任何单独的字符或正则表达式。 与FS等效的是OFS,它表示输出分隔符,默认值也是一个空格。

系统变量NF,为当前输入记录的字段个数,改变NF的值会有副作用,$NF代表最后一个Field(列) 不要与 NF搞混了。

系统变量FILENAME,为当前输入文件的名称,当应用多个输入文件时,系统变量FNR被用来表示与当前输入文件相关记录行。

系统变量RS,为记录分隔符它的默认值是一个换行符,与RS等效的是ORS

特殊变量

最后的字段$NF

awk -F : '{print $NF}' /etc/passwd

NR当前行号:

# 默认
docker images | awk '{print $2,$3}'

TAG IMAGE
1.0 98c79a7bb07f

# 输出行号大于1的行
docker images | awk 'NR > 1 {print $2,$3}'

1.0 98c79a7bb07f

条件判断

条件语句以 if 开头,计算圆括号中的表达式

# 语法
if ( expression )
   action1
[else
   action2]

如果条件表达式 expression 的值为真(非零或),执行 action1 ,当存在 else 语句时,如果值为假(零或空)则执行 action2。 一个条件表达式可能包含算数运算符,关系运算符,或布尔运算符 。

测试变量是否非零值,如果是非零值则打印X

if ( x ) print x

测试是否相等

if ( x == y ) print z

测试是否匹配

if ( x ~ /[yY](es)?/ ) print x

如果有多个语句组成的,需要用一对大括号将这些语句括起来,如果使用分号后面的换行符则是可选的。

if ( expression ) {
  action1;
  action2;
}

循环语句

循环是一种用于重复执行一个或多个操作的结构,awk中可以使用 while,do,或者 for 语句。

while循环

while ( condition ) {
   action
}

条件表达式在循环的顶部进行计算,如果为真则执行循环体action部分,相反则不执行循环体;

i = 1
while ( i <= 4 ) {
    print $i
    ++i
}

do 循环

do {
    action
while ( condition )

示例

BEGIN {
    do {
        ++x
        print x
    } while ( x <= 4 )
}

注意,dowhile 循环之间的区别,do 循环至少执行一次循环体。

for 循环

for ( set_counter; test_counter; increment_counter ) {
    action
}

for 循环由3个表达式组成

set_counter         计数器初始值
test_counter        测试条件
increatment_counter 递增计数器

递加

for ( i=1; i<=NF; i++) {
    print $i
}

递减

for ( i=NF; i>=1; i--) {
    print $i
}

其它影响流控的语句

  • break 语句,退出循环。
  • continue 语句,跳过当前循环进入下一次循环。
  • Next 语句,读入下一个输入行。
  • Next语句,的典型应用是结合系统变量FILENAME(当前文件名)连续从文件读取内容,忽略其它的操作,直到文件全部读完。
  • exit语句,将退出主输入循环并且跳到END部分;如果没有定义END部分,或者END中使用exit 语句,则终止脚本执行。

exit语句,也可以作为表达式的返回状态,如果没有提供表达式,那么返回0,如果为exit 设置一个值,则awk 退出状态为此值。

echo "" | awk '{exit}' ; echo $?
0

echo "" | awk '{ exit 1}' ; echo $?  #注意exit 1
1







回到页面顶部