shell语法 05-Linux文本处理-awk

  • awk是一种编程语言
  • 适合文本处理和报表生成
语法格式
awk	[option] 'pattern{action}' file
awk [参数]    '条件{动作}'       文件

awk命令的参数选项

  • -F 指定字段分隔符
  • -v 定义或修改一个awk内部的变量

awk命令的常见功能

  • 指定分隔符显示某几列
# 直接取出显示出日志文件的url的这一列
awk -F "GET|HTTP" '{print $2}' access.log
  • 通过正则表达式取出你想要的内容
# 分析生产环境中的日志找出谁在破解用户密码
awk '$6~/Failed/{print $11}' /var/log/secure
  • 显示出某个范围内的内容
# 显示文件的20到30行
awk 'NR==20,NR==30' filename
  • 通过awk进行统计计算
# 计算总和
awk '{sum+=$0}END{print sum}' test.txt
  • awk数组计算与去重
# 对日志进行统计与计数
awk '{array[$1]++}END{for(key in array)print key,array[key]}' access.log

指定分隔符显示某几列

# 输出passwd的每一行
awk '{print $0}' /etc/passwd
# 针对每行以‘:’为分割符,输出第一项
awk -F":" '{ print $1 }' /etc/passwd
# 针对每行以‘:’为分割符,输出第一项和第二项
awk -F":" '{ print $1 $3 }' /etc/passwd
# 针对每行以‘:’为分割符,输出第一项和第二项,并格式化输出
awk -F":" '{ print $1 " " $3 }' /etc/passwd
awk -F":" '{ print "username: " $1 "		uid:" $3 }' /etc/passwd

BEGIN和END模块

  • 在 awk 开始处理输入文件中的文本之前,执行初始化代码(BEGIN内的代码块)。
  • awk 在处理了输入文件中的所有行之后执行END块。
  • END 块用于执行最终计算或打印应该出现在输出流结尾的摘要信息。

赋值运算符

[root@yum tmp]# awk 'BEGIN{a=5;a+=5;print a}'
10

逻辑运算符

[root@yum tmp]# awk 'BEGIN{a=1;b=2;print (a>2&&b>1,a=1||b>1)}'
0 1

正则运算符

[root@yum tmp]# awk 'BEGIN{a="100testaaa";if(a~/100/){print "ok"}}'
ok
[root@yum tmp]# echo|awk 'BEGIN{a="100testaaa"}a~/100/{print "ok"}'
ok

关系运算符

  • 其 > < 可以作为字符串比较,也可以用作数值比较,
  • 关键看操作数如果是字符串,就会转换为字符串比较。
  • 两个都为数字 才转为数值比较。
  • 字符串比较:按照ascii码顺序比较。
[root@yum tmp]# awk 'BEGIN{a=11;if(a>=9){print "ok"}}'
ok
[root@yum tmp]# awk 'BEGIN{a;if(a>=b){print "ok"}}'
ok

算术运算符

  • 所有用作算术运算符进行操作,操作数自动转为数值,
  • 所有非数值都变为0。
[root@yum tmp]# awk 'BEGIN{a="b";print a++,++a}'
0 2
[root@yum tmp]# awk 'BEGIN{a="20b4";print a++,++a}'
20 22

其他运算符

  • ?:三目运算符
[root@yum tmp]# awk 'BEGIN{a="b";print a=="b"?"ok":"err"}'
ok
[root@yum tmp]# awk 'BEGIN{a="b";print a=="c"?"ok":"err"}'
err

awk内置变量

变量名 属性
$0 当前记录
$1~$n 当前记录的第n个字段
FS 输入字段分隔符,默认是空格
RS 输入记录分隔符,默认为换行符
NF 当前记录中的字段个数,就是有多少列
NR 已经读出的记录数,就是行号,从1开始
OFS 输出字段分割符,默认是空格
ORS 输出记录分隔符,默认为换行符

字段分隔符 FS

  • FS=" +" 一个或多个 Tab 分隔
[root@yum tmp]# cat tab.txt
ww		CC		IDD

[root@yum tmp]# awk 'BEGIN{FS="	+"}{print $1,$2,$3}' tab.txt
ww CC IDD

  • FS="[[:space:]+]" 一个或多个空白空格,默认的
[root@yum tmp]# cat space.txt
we are           studing awk now!
[root@yum tmp]# awk -F [[:space:]+] '{print $1,$2}' space.txt
we are
  • FS="[" ":]+" 以一个或多个空格或:分隔
[root@yum tmp]# cat hello.txt
root:x:0:0:root: /root:/bin/bash

[root@yum tmp]# awk -F [" ":]+ '{print $1,$2,$3}' hello.txt
root x 0

字段数量 NF

  • 已":"为分隔符,分隔字段数量NF为8个则输出该行
[root@yum tmp]# cat hello.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin:888

[root@yum tmp]# awk -F ":" 'NF==8{print $0}' hello.txt
bin:x:1:1:bin:/bin:/sbin/nologin:888

记录数量 NR

  • 以一个或多个空格或:分隔得出的结果取第二行
[root@yum tmp]# ifconfig eth0| awk -F [" ":]+ 'NR==2{print $4}'
192.168.10.10

RS 记录分隔符变量

  • 将 FS 设置成" "告诉 awk 每个字段都占据一行。
  • 通过将 RS 设置成"",告诉 awk每个地址记录都由空白行分隔。
[root@yum tmp]# cat recode.txt
Jimmy the Weasel
100 Pleasant Drive
San Francisco, CA 12345
#此处是空白行
Big Tony
200 Incognito Ave.
Suburbia, WA 67890

[root@yum tmp]# cat awk.txt
#!/bin/awk
BEGIN {
    FS="
"
    RS=""
}
{
    print $1 ", " $2 ", " $3
}

[root@yum tmp]# awk -f awk.txt recode.txt
Jimmy the Weasel, 100 Pleasant Drive, San Francisco, CA 12345
Big Tony, 200 Incognito Ave., Suburbia, WA 67890

OFS 输出字段分隔符

[root@yum tmp]# cat hello.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin:888

[root@yum tmp]# awk 'BEGIN{FS=":"}{print $1","$2","$3}' hello.txt
root,x,0
bin,x,1

[root@yum tmp]# awk 'BEGIN{FS=":";OFS="#"}{print $1,$2,$3}' hello.txt
root#x#0
bin#x#1

ORS 输出记录分隔符

[root@yum tmp]# cat recode.txt
Jimmy the Weasel
100 Pleasant Drive
San Francisco, CA 12345

Big Tony
200 Incognito Ave.
Suburbia, WA 67890

[root@yum tmp]# cat awk.txt
#!/bin/awk
BEGIN {
    FS="
"
    RS=""
    ORS="

"
} 
{
    print $1 ", " $2 ", "$3
}

[root@yum tmp]# awk -f awk.txt recode.txt
Jimmy the Weasel, 100 Pleasant Drive, San Francisco, CA 12345
Big Tony, 200 Incognito Ave., Suburbia, WA 67890

规则表达式

  • awk '/REG/{action} ' file
  • /REG/为正则表达式,
  • 可以将$0 中,满足条件的记录送入到:action 进行处理
[root@yum tmp]# awk '/root/{print $0}' passwd
root:x:0:0:root: /root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[root@yum tmp]# awk -F : '$5~/root/{print $0}' passwd
root:x:0:0:root: /root:/bin/bash
[root@yum tmp]# ifconfig eth0|awk 'BEGIN{FS="[[:space:]:]+"} NR==2{print $4}' #取出 ip
192.168.10.10
[root@yum tmp]# ifconfig eth0|awk 'BEGIN{FS="([[:space:]]|:)+"} NR==2{print $4}' #取出 ip
192.168.10.10

布尔表达式

  • awk '布尔表达式{action}' file 仅当对前面的布尔表达式求值为真时, awk 才执行代码块。
[root@yum tmp]# awk -F: '$1=="root"{print $0}' passwd
root:x:0:0:root: /root:/bin/bash
[root@yum tmp]# awk -F: '($1=="root")&&($5=="root"){print $0}' passwd
root:x:0:0:root: /root:/bin/bash

条件语句

{
    if ( $1== "foo" ) {
        if ( $2== "foo" ) {
            print "uno"
        } else {
            print "one"
        }
    } elseif ($1== "bar" ) {
        print "two"
    } else {
        print "three"
    }
}

循环结构

do...while循环
{
    count=1
    do {
        print "I get printed at least once no matter what"
    } while ( count !=1 )
}
for 循环
{
    for ( x=1;x<=4;x++ ) {
        print "iteration", x
    }
}

break和continue

{
    x=1
    while (1) {
        if ( x==4 ) {
            x++
            continue
        }
        print "iteration", x
        if ( x>20 ) {
            break
        }
    x++
}

数组

{
    cities[1]=”beijing”
    cities[2]=”shanghai”
    cities[“three”]=”guangzhou”
    for( c in cities) {
        print cities[c]
    }
    print cities[1]
    print cities[“1”]
    print cities[“three”]
}

例子

查看服务器连接状态并汇总
netstat -an|awk '/^tcp/{++s[$NF]}END{for(a in s)print a,s[a]}'
统计 web 日志访问流量
要求输出访问次数
请求页面或图片
每个请求的总大小
总访问流量的大小汇总

awk '{a[$7]+=$10;++b[$7];total+=$10}END{for(x in a)print b[x],x,a[x]|"sort -rn -k1";print
"total size is :"total}' access_log

a[$7]+=$10表示以第7列为下标的数组($10列为$7列的大小)
把他们大小累加得到$7每次访问的大小
后面的for循环有个取巧的地方,a和b数组的下标相同
所以一条for语句足矣

awk常用函数

函数 说明
gsub( Ere, Repl, [ In ] ) 除了正则表达式所有具体值被替代这点,它和 sub 函数完全一样地执行。
sub( Ere, Repl, [ In ] ) 用 Repl 参数指定的字符串替换 In 参数指定的字符串中的由 Ere参数指定的扩展正则表达式的第一个具体值。 sub 函数返回替换的数量。出现在 Repl 参数指定的字符串中的 &(和符号)由 In 参数指定的与 Ere 参数的指定的扩展正则表达式匹配的字符串替换。如果未指定 In 参数,缺省值是整个记录( $0 记录变量)。
index( String1, String2 ) 在由 String1 参数指定的字符串(其中有出现 String2 指定的参数)中,返回位置,从 1 开始编号。如果 String2 参数不在 String1 参数中出现,则返回 0(零)。
length [(String)] 返回 String 参数指定的字符串的长度(字符形式)。如果未给出String 参数,则返回整个记录的长度( $0 记录变量)。
blength [(String)] 返回 String 参数指定的字符串的长度(以字节为单位)。如果未给出String 参数,则返回整个记录的长度( $0 记录变量)。
substr( String, M, [ N ] ) 返回具有 N 参数指定的字符数量子串。子串从 String 参数指定的字符串取得,其字符以 M 参数指定的位置开始。 M 参数指定为将String 参数中的第一个字符作为编号 1。如果未指定 N 参数,则子串的长度将是 M 参数指定的位置到 String 参数的末尾 的长度。
match( String, Ere ) 在 String 参数指定的字符串( Ere 参数指定的扩展正则表达式出现在其中)中返回位置(字符形式),从 1 开始编号,或如果 Ere 参数不出现,则返回 0 (零)。 RSTART 特殊变量设置为返回值。 RLENGTH特殊变量设置为匹配的字符串的长度,或如果未找到任何匹配,则设置为 -1(负一)。
split( String, A, [Ere] ) 将 String 参数指定的参数分割为数组元素 A[1], A[2], . . ., A[n],并返回 n 变量的值。此分隔可以通过 Ere 参数指定的扩展正则表达式进行,或用当前字段分隔符( FS 特殊变量)来进行(如果没有给出 Ere参数)。除非上下文指明特定的元素还应具有一个数字值,否则 A 数组中的元素用字符串值来创建。
tolower( String ) 返回 String 参数指定的字符串,字符串中每个大写字符将更改为小写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。
toupper( String ) 返回 String 参数指定的字符串,字符串中每个小写字符将更改为大写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。
sprintf(Format, Expr,Expr, . . . ) 根据 Format 参数指定的 printf 子例程格式字符串来格式化 Expr参数指定的表达式并返回最后生成的字符串。

替换

awk 'BEGIN{info="this is a test2010test!";gsub(/[0-9]+/,"!",info);print info}' this is a test!test!
在info中查找满足正则表达式,/[0-9]+/用”!”替换,并且替换后的值,赋值给 info未给info值,默认是$0

查找

awk 'BEGIN{info="this is a test2010test!";print index(info,"test")?"ok":"no found";}'ok 
#未找到,返回 0

匹配查找

awk 'BEGIN{info="this is a test2010test!";print match(info,/[0-9]+/)?"ok":"no found";}'ok 
#如果查找到数字则匹配成功返回 ok,否则失败,返回未找到

截取

awk 'BEGIN{info="this is a test2010test!";print substr(info,4,10);}'s is a tes 
#从第 4 个 字符开始,截取 10 个长度字符串

分割

awk 'BEGIN{info="this is a test";split(info,tA," ");print length(tA);for(k in tA){print k,tA[k];}}' 44 test 1 this 2 is 3 a
#分割info,动态创建数组tA,awk for …in 循环,是一个无序的循环。并不是从数组下标1…n 开始
原文地址:https://www.cnblogs.com/liangjingfu/p/9419412.html