awk(4)-awk介绍

1介绍
1.1 特点:awk是一种编程语言,可以直接运行不用提前编译;有内建的pipe 功能,可以将处理中数据传送给shell 处理,再将shell 处理结果传回awk,pipe让awk 能容易的使用系统资源。
通常写一些小工具,由shell 所提供的pipe 将数据传送给不同的awk 工具处理,从而解决大的问题。如果有性能要求,这些小工具也可以用c语言来改写。
sed 是处理一行的数据,awk是将一行分成几个段来处理。

1.2 语法:
awk '条件类型1 {指令1} 条件类型2{指令 2} ...' filename
awk 后面接单引号并加上大括号来设定要对数据进行的处理。后面可以接档案,也可以使用标准输入。awk 处理的是每一行的字段的数据,默认是用空格或者是Tab 键作为分割符。

1.3 awk 处理流程:
1、读取一行,将数据放入 $0,$1,$2.....   $0 是整行数据,$1,$2,$3... 就是按照分割符分割的一列列数据
2、依据条件类型,判断是否执行后面的“指令”。判断条件的值为ture(或者不为0,或者不是空字符串)。如果大括号前面没有“条件”,则指令被无条件的执行。
3、做完所有的“指令”与“条件类型”
4、如果还有行数据,重复1--3步骤,直到所有数据都被处理完。
5、如果有多个文件,则一个一个文件分别执行
6、多个脚本一个文件,一行数据顺序执行脚本中的指令

1.4条件类型
1.4.1、  awk 逻辑运算字符
 >        <        >=        <=        ==        !=
如:x>34{commond}
1.4.2、  ~ 匹配,!~ 不匹配
如:A~B 字符串A是否包含B
"banana"~/an/ 成立
1.4.3 &&,||,! 
上面两种情况条件,结果是一个逻辑值,可以和 &&,||,!  结合成一个新的逻辑值
1.4.4 两个条件
示例:
FNR>=22 && FNR<=28{print "    " $0}
可以写成
FNR==22,FNR==29{print "    " $0}
说明:这种条件类型,awk会设立一个switch,当第一个条件成立时,打开这个switch执行命令,当第二个条件成立时,就关闭这个switch命令就不执行了

1.5 awk的指令
http://blog.csdn.net/convict_eva/article/details/74988695


1.6 awk内置变量 
1)、NF 每一行($0) 拥有的列数(使用分割符分割)
2)、NR 目前处理的第几行数据(从1开始计数),如果有多个文件,则这个值一直累加。
3)、FNR 和NR的区别是:每打开一个文件 FNR 重新开始累计
4)、FS 目前的分割符,默认是空格( 或者tab)
5)、OFS 列输出分割符。默认中空格。  print $1,$2
6)、RS 行分割符。默认是换行(\n)
7)、ORS 输出时行的分割字符。默认是换行(\n)
8)、FILENAME  正在处理的文件名称(awk 后面的文件是参数是什么,这个这是就是什么。如果是从管道接收数据,值为-(标准输入))
9)、OFMT   数值输出格式,默认是%.6g。即最多输出6位小数
如:print 2/3  输出:0.666667  
RSTART   RLENTTH 参考awk 的match()函数  http://blog.csdn.net/convict_eva/article/details/74987793
10)、SUBSEP 数组是标注分隔元。默认为\034
实际上awk中数组只接受字符串为标注。如:arr["jamin"],在使用时awk仍可以使用数字,甚至可以使用多级数组。如:arr[1,22] 事实上,awk 接受arr[1,22]之前,先把其标注替换成了字符串"1\03422",之后使用arr["1\03422"] 代替arr[1,22]
11)、ARGV[],ARGC
ARGC   是一个整数,除了选项 -v -f 及基对应的参数之外的参数数目。可以用ARGC 来判断开启文件数量,但是这个值是可以修改的(如:ARGC=1)。当ARGC 值被设置为1时,awk
误以为没有文件要处理,不能 ARGV[1],ARGV[2]来打开文件,但是还是可以通过ARGV[1],ARGV[2] 来取得命令行上的参数。如果awk 命令后面有多个文件时,设置ARGC 值来处理多个文件。
ARGV[]  字符串数据,代表输入参数。从0开始计数,ARGV[0] 是awk,参数还是从1开始计数,类似shell 脚本。
示例打印参数:arg.awk 脚本如下:
awk 'BEGIN{
        for(i=0;i<ARGC;i++){
            print ARGV[i]
        }
}' $*
执行:
$./arg.awk a b c de
awk
a
b
c
de

示例:
$ echo 'A125 Jenny 100 210' | awk '{print $0"\n" $1, $2,$3,$4 "\n" "NF="NF"," "NR="NR "," "FILENAME="FILENAME ",FS="FS}'
A125 Jenny 100 210     #$0
A125 Jenny 100 210     #$1,... $4
NF=4,NR=1,FILENAME=-,FS=
说明:FILENAME=- 没有指定文件,是从流中直接取的数据
NF 当前有4列,所以值为4
NR 当前只有一行,所以值为1
FS 默认是空格,可以在命令行执行后通过选择可以看出来后面有个空格。
print 是awk输出指令,默认输出到屏幕。输出的屏幕是以空格(OFS 内置变量,可以设置,默认是空格)隔开,如上面结果的第二行。


2 使用awk
2.1 将awk 指令写在文件中,执行语法如下:
$awk -f test.awk file_name
$awk -f test.awk -f test2.awk file_name  (一行数据,先执行test.awk 中的指令,再执行test2.awk 中的指令 )
示例:
有pay文件内容如下:
A125 Jenny 100 210
A341 Dan 110 215
P158 Max 130 209
P148 John 125 220
A123 Linda 95 210
说明:
第一列:员工编号:A是组装部门,P 是包装部门
第二列:员工姓名
第三列:工资
第四列:工时
例1:组装部门工资上调5%,所有(不只是组装部门的)员工工资低于100,则调整为100.
$ awk '$1~/^A.*/{$3 *= 1.05} $3<100{$3=100}{print $1,$2,$3,$4}' pay
A125 Jenny 105 210
A341 Dan 115.5 215
P158 Max 130 209
P148 John 125 220
A123 Linda 100 210
也可把awk 命令写入文件(wage.awk,写在文件中的命令是可以格式化的,在下面的示例中可以看到。):

$1~/^A.*/ {$3 *= 1.05}
$3<100 {$3=100}
{print $1,$2,$3,$4}
执行如下命令,结果也是一样的:
$ awk -f wage.awk pay

2.2 在awk 中使用数组(书本中收阵列,类似键值)
awk中数组的特性:
1)、以字符串为标注,而不是数字(可以使用数字,awk内部自动转化)。如:arr["a"]=1
2)、不用提前声明数组。(使用变量也是不用提前声明)
示例:
有学生选择的课程文件reg如下:
Mary O.S. Arch. Discrete
Steve D.S. Algorithm Arch.
Wang Discrete Graphics O.S.
Lisa Graphics A.I.
Lily Discrete Algorithm
第一列是姓名,之后的列是选择的课程。现在要统计每个课程选择的人数:
awk 处理命令文件 course.awk 内容如下:

{
    for( i=2;i<NF;i++){
        Number[$i]++
    }
}
END {
    for(course in Number){
        print course,Number[course]
    }
}
$ awk -f course.awk reg 
Discrete 2
O.S. 1
D.S. 1
Graphics 2
Algorithm 1
Arch. 1
说明:
1、在course.awk 文件中,使用Number[] 不需要提前声明;
2、第一个指令,每一行第从第二列开始,用课程名做数据下标,开始计数。Number[$i]++ , Number[$i]=Number[$i]+1 这两种写法一样
3、awk 中以$ 开头的变量,都可以理解为:i=2 ;$i=$2,表示第二列数据
4、第二个指令是对数据的遍历,下标是课程名称,值是选择课程的学生数量
5、END 是AWK 保留字,END 也是条件表达示的一种。END 成立条件是awk 处理完所有的数据,将要退出awk程序时
6、BEGIN 是awk 中另一个保留字。也是一个条件表达式,与END对应。BEGIN 对应的指令,在awk程序开始执行之前(读取第一行数据之前),被执行一次。如:修改默认的分割符。


2.3 awk 调用shell 脚本命令
awk 允许调用shell 脚本,并且可以使用pipe 来和系统间传递数据。
示例:统计在线人数
awk 脚本online.awk 内容如下:
BEGIN{
    while("who"|getline) n++
    print n
}
$ awk -f online.awk 
说明:
1)、awk 不一定要处理文本内容,上面的执行的awk 就没有对应的文件名
2)、| 为awk 的pipe 符号。awk 把pipe 之前的命令"who" 当成shell上的命令,并将命令送往shell 执行。执行完后的结果通过pipe 送回awk程序中。
3)、awk input 指令只有一个:getline 。输出指令有两个:print   printf
awk getline 参考 http://blog.csdn.net/convict_eva/article/details/74989777


2.4 输出内容到文件
> 输出到新产生文件  >> 追加到文件
示例:
outfile.awk 脚本如下:
BEGIN{
    print "this is test line." > "outfile1"
    print "this is first line." > "outfile1"
    print "this is second line." >> "outfile1"
}
$ awk -f outfile.awk 
执行结果,三行数据都会写入outfile1 文件。
说明:
1、档案名要以"" 括住,如果不括住则是一个变量。awk使用变量,不用提前声明,其值为空字符串或0
2、awk 使用 > ,第一次执行时会产生一个新的文件,之后再执行时就是追加到这个文件;并非每一次都产生一个新的文件(把原有的文件清空)
使用>> 时,若文件已存在,则追加;如果文件不存在则新建文件,再追加。
所以上面的命令,不管执行多少次,outfile1 文件只有3行。如果把上面的脚本中的 > 替换成 >> 那么,执行的输出都会保存下来。


2.5 使用系统资源
有sort 文件如下:
1 9
3 7
5 5
7 3
2 8
4 6
6 4
8 2
0 10
按照第二行排序输出到文件outfile1
$ awk '{print $1,$2 | "sort -n -k 2 -t\" \" >> outfile1"}' sort;
说明:不管有多少行,awk 执行完所有的print 后,才把数据一次送给shell 进行排序处理。


2.6 将awk程序写在shell脚本中
示例:模拟cat 命令
awk 命令p文件内容如下:

awk '{
    print
}' $* 
$./p test
说明:
1)p 文件要有执行权限
2)p 文件中有两个空格是必须的。i:第一行,awk和' 之间的空格;ii:}'和$* 之间的空格。
3)shell 中并没有awk 指令,awk程序要包裹在 '' 中。
4)awk 程序中一律使用"" 包裹字符,不使用'',以免和shell 混淆。
5)$* 是shell 中用法,表示指令后面的所以参数。
6)可以指定多个文件,awk 会按照文件的顺序处理文件。如果文件无法打开(不存在,没有权限)会报错。(如果使用BEGIN 条件,不会打开文件就执行。所以文件不存在,也不会报错。)
7)如果执行awk 不接任何档案名,则将把STDIN 视为输入资料的来源。
如:执行
$./p

$awk '{print}'   #即print $0 ,$0 是可以不写的
则输入什么就打印什么。

2.7 修改awk的分割符
2.7.1 awk 默认是空格(或者tab来切割的),可以设置FS值来修改分割的字符串(一定要在BEGIN 中设置,否则第一行数据不起作用)。
示例:
$ awk 'BEGIN{FS=":"} {print $1}' /etc/passwd   # 如果不加BEGIN ,则第一行数据打印是完整的。

2.8 awk 调用函数
2.8.1 awk 内部函数
示例:

awk '
BEGIN{
    x=1
    y=2
    test_f(x)
    print a,x,y
}
function test_f(x){
    a=3
    x+=1
    y=3
    print a,x,y
}
' $* 
执行结果:
3 2 3
3 1 3
说明:
1)函数内可以使用主程序中的任何变量
2)函数内的变量(参数除外)在函数之外也可以使用
3)不管函数内还是主程序中的变量,只要变量名相同,就被认为是一个上变量(函数参数除外)
4)这种特性不好处是,调用了函数,修改了主程序中的变量本身。可以通过传递参数的方式,如示例中的x 值,在函数中修改后,没有影响到主程序中的值。

2.8.2 使用system("cmd") 调用shell 定义的函数

#!/bin/bash
function test(){
    echo "this is test" $1
}
export -f test
awk '
    BEGIN{
        system("test abc")
        "test abc" | getline r;print "r:",r
    }
'
this is test abc
r: this is test abc
说明:pipe 把shell 命令的标准输出作为awk的输入

2.9 处理多行。

awk 内置变量RS(默认为\n)是用来分割行数据的,也就是说用RS 的值把输入的文档分割成一行一行的处理。可以修改这个值,来改变awk分割行的方法。
2.9.1、RS="" 会把文档中的空白行,当着分割的行。
示例:
有sign 文件如下(空白处的空白的行):

name   time
zhang  8:50


li     9:00
wang   9:10

zhou   8:10

xie    9:30
按照空白行分割,打印行:
$ awk 'BEGIN{RS=""}{print $0,NR,"line"}' sign
name   time
zhang  8:50 1 line
li     9:00
wang   9:10 2 line
zhou   8:10 3 line
xie    9:30 4 line
1、可以看出使用空行分割开的多行,当着了一行处理。
2、awk 分忽略文档前后的空白行。
3、把\n 看着一个字符就好理解了。那么把FS设置为\n ,就可以处理一行一行的数据了。
4、RS 也支持正则

使用:查找文件中关键字,输出前10到后15行

awk '
BEGIN{
    file_name=ARGV[1]
    keyword=ARGV[2]
    while("cat -n " file_name " | grep " keyword " | awk \x27{print $1}\x27 " | getline num){
        printLine(num)
    }
}
function printLine(num){
    start=num-10
    end=num+15
    print "----------------------------------------------------------------------"
    system("sed -n \x27"start","end "p\x27 " file_name)
    print "----------------------------------------------------------------------"
}
' $*
执行:
$./awk file_name keyword
说明: 
1、\x27 就是单引号


猜你喜欢

转载自blog.csdn.net/convict_eva/article/details/75013572
awk