AWK-入门指南

若如初见. 提交于 2020-01-11 01:35:22

Awk是一种便于使用且表达能力强的程序设计语言,可应用于各种计算和数据处理任务

1.1起步

加入我们现在有一个文件 文件名emp.txt 里面存放的是员工的每小时的薪资 每个员工单独占一行 如下所示

Beth    4.00    0
Dan     3.75    0
kathy   4.00    10
Mark    5.00    20
Mary    5.50    22
Susie   4.25    18

现在你想打印出工作时间超过零小时的员工的姓名和工资(薪资乘以时间)。
使用命令及输出内容

[root@ localhost ~]# awk '$3 >0 { print $1,$2 * $3}' emp.txt
kathy 40
Mark 100
Mary 121
Susie 76.5

命令讲解:$ > 3 是我们的判断条件 { print $1,$2 * $3 } 这个是我们想要输出的内容 包括员工的名字以及工资 emp.txt 是我们要读取的文件名字

打印工作时间等于0 个小时的工作人员

使用命令 及输出结果

[root@ localhost ~]# awk '$3 == 0 { print $1 }' emp.txt
Beth
Dan

打印名字叫kathy 并且工作小时大于0 的名字 和薪资总和
比如

[root@ localhost ~]# awk '$3==0' emp.txt
Beth    4.00    0
Dan    3.75    0

1.2简单输出

打印每一行
如果一个动作没有任何模式 ,这个动作会对搜友输入的行进行操作,print语句用来输出当前输入的行

[root@ localhost ~]# awk '{print}' emp.txt
Beth    4.00    0
Dan    3.75    0
kathy    4.00    10
Mark    5.00    20
Mary    5.50    22
Susie    4.25    18

输出所有输入的内容到标准输出由 $0表示整行

[root@ localhost ~]# awk '{print $0}' emp.txt
Beth    4.00    0
Dan    3.75    0
kathy    4.00    10
Mark    5.00    20
Mary    5.50    22
Susie    4.25    18

打印特定字段

[root@ localhost ~]# awk '{print $1,$3}' emp.txt
Beth 0
Dan 0
kathy 10
Mark 20
Mary 22
Susie 18

在print语句种被逗号分隔的表达式,在默认情况下 他们会将用一个空格分割来输出,每一行print生成的内容都会以一个换行字符作为结束,但这些默认行为都可以自定义。

NF,字段数量

依次打印出每一行的字段数量,第一个字段数量,最后一个字段的值:
[root@ localhost ~]# awk '{print NF,$1,$NF}' emp.txt
3 Beth 0
3 Dan 0
3 kathy 10
3 Mark 20
3 Mary 22
3 Susie 18

计算和打印

打印出姓名和员工的合计支出(以小时计算)
[root@ localhost ~]# awk '{print $1,$2*$3}' emp.txt
Beth 0
Dan 0
kathy 40
Mark 100
Mary 121
Susie 76.5

打印行号

AWK提供了一个内建变量,叫做NR,它会存储当前已经读取了多少行的计数,我们可以使用NR和$0给文件每一行加上行号

[root@ localhost ~]# awk '{print NR,$0}' emp.txt
1 Beth    4.00    0
2 Dan     3.75    0
3 kathy   4.00    10
4 Mark    5.00    20
5 Mary    5.50    22
6 Susie   4.25    18

在输出中添加内容

当然也可以在字段中间或者计算的值中间打印输出想要的内容:
[root@ localhost ~]# awk '{print "total pay for",$1,"is",$2*$3}' emp.txt
total pay for Beth is 0
total pay for Dan is 0
total pay for kathy is 40
total pay for Mark is 100
total pay for Mary is 121
total pay for Susie is 76.5

1.3高级输出

print语句可用于快速而简单的输出。若要严格按照你所想的格式化输出,则需要使用print语句。print几乎可以生产任何形式的输出。
字段排队
printf语句的形式如下:

printf(format,value1,value2,……,valuen)

其中format是字符串,包含要逐字打印的文本,穿插着format之后的每个值该如何打印规则。一个规则是一个%符,后面跟着一些字符,用来控制一个value的格式,第一个规则说明如何打印value1,第二个说明如何打印value2…因此,有多少个value要打印,在format中就要有多少个%规格。

使用printf打印每位员工的总薪资

[root@ localhost ~]# awk '{printf("total pay for %s is $%.2f\n",$1,$2*$3)}' emp.txt 
total pay for Beth is $0.00
total pay for Dan is $0.00
total pay for kathy is $40.00
total pay for Mark is $100.00
total pay for Mary is $121.00
total pay for Susie is $76.50

注意:printf不会自动产生空格或者新的行,需要自己来创创建

打印每位员工的姓名与薪资

[root@ localhost ~]# awk '{printf("%-8s $%6.2f\n",$1,$2*$3)}' emp.txt 
Beth     $  0.00
Dan      $  0.00
kathy    $ 40.00
Mark     $100.00
Mary     $121.00
Susie    $ 76.50

第一个规格%-8s将一个姓名以字符串形式在8个字符宽度的字段中左对齐输出。
第二个规格%6.2f 将薪资以数字的形式,保留小数点后两位 在6个字符宽度的字段中输出

排序输出
假设你想打印每位员工的所有数据,包括他或他的薪酬,并以薪资递增的方式进行排序输出。最简单的方式是使用awk将每位员工的总薪资置于其记录之前,然后利用有一个排序程序来处理AWK的输出。

不同的系统 这个是在Unix上

awk '{ printf("%6.2f    %s\n", $2 * $3, $0) }' emp.data | sort

  0.00    Beth  4.00 0
  0.00    Dan   3.75 0
 40.00    Kathy 4.00 10
 76.50    Susie 4.25 18
100.00    Mark  5.00 20
121.00    Mary  5.50 22

1.4选择

AWK的模式适合用于为进一步的处理从输入中选择相关的数据行。由于不带动作的模式会打印所有匹配模式的行,所有很多AWK程序仅包含一个模式。

通过对比选择

这个程序使用一个对比模式来来选择每个小时赚5美元或更过的员工记录,也就是 第二个字段大于等于5的行

[root@ localhost ~]# awk '$2>=5' emp.txt 
Mark    5.00    20
Mary    5.50    22

通过计算选择

打印出总薪资超过50美元的员工的薪资。

[root@ localhost ~]# awk '$2*$3>50 {printf("$%.2f for %s\n",$2*$3,$1)}' emp.txt 
$100.00 for Mark
$121.00 for Mary
$76.50 for Susie

通过文本内容选择

除了数值测试,你还可以选择包含特定单词或短语的输入行。这个程序会打印所有第一个字段为Susie的行

[root@ localhost ~]# awk ' $1=="Susie" ' emp.txt 
Susie    4.25    18‘

操作符 == 用于测试相等性。你也可以使用称为正则表达式的模式查找包含任意字母组合,单词或短语的文本。

[root@ localhost ~]# awk '/Susie/' emp.txt 
Susie    4.25    18

模式组合
可以使用括号和逻辑操作符与&& 或|| ,以及非!对模式进行组合

[root@ localhost ~]# awk '$2>=4||$3>=20' emp.txt
Beth     4.00    0
kathy    4.00    10
Mark     5.00    20
Mary     5.50    22
Susie    4.25    18
打印既不满足$2 小于4 也不满足 $3小于20的行;这个条件与上面第一个模式组合等价
[root@ localhost ~]# awk '!($2<4 && $3 <20 )' emp.txt 
Beth    4.00    0
kathy    4.00    10
Mark    5.00    20
Mary    5.50    22
Susie    4.25    18

数据验证
实际的数据中总是会存在错误的。再数据验证-检查数据的值是否合理以及格式是否正确方面,AWK是个优秀的工具。

数据验证本质上是否定的:不是打印具备所期望属性的行,二十打印可疑的行。如下程序使用对比模式将5个数据合理性测试应用w文件的每一行

awk 'NF!=3 {print $0,"number of fields is not equal to 3"}' emp.txt
awk '$2<3.35 {print $0, "rate is below minimum wage"}' emp.txt
awk '$2>10 {print $0, "rate exceeds $10 per hour"}' emp.txt
awk '$3 <0 {print $0, "negative hours worked"}' emp.txt 
awk '$3 >60 {print $0,"too many hours worked"}' emp.txt 

如果没有错误 是没有输出的,因为这个个文件中的内容没有满足的条件

BEGIN与END

特殊模式BEGIN用于匹配第一个输入文件的第一行之前的位置,END则用于匹配处理过的最后一个文件的最后一行的位置。这个程序使用BEGIN来输出一个标题

[root@ localhost ~]# awk 'BEGIN {print "name    RATE   HOURS";print ""} {print}' emp.txt 
name    RATE   HOURS

Beth    4.00    0
Dan     3.75    0
kathy   4.00    10
Mark    5.00    20
Mary    5.50    22
Susie   4.25    18

程序的动作部分你可以再一行上放多个语句,不过要使用分号进行分割。注意普通的print是打印当前输入行,与之不同的是print “” 会打印一个空行。

1.5 使用AWK进行计算

 一个动作就是一个以新行或者分号跟个的语句序列。你已经见过一些其动作仅是个单个print语句的例子。本节将提供一些执行简单的数值以及字符串计算的数据示例。在这些语句中,你不仅可以使用像NF这样的内置变量,还可以创建自己的变量用于计算、存储数据诸如此类的操作。AWK中,用户创建的变量不需要声明。

计数

这个程序使用一个变量emp来统计工作超过15个小时的员工的数目

[root@ localhost ~]# awk '$3 > 15 {emp=emp+1} END {print emp, "employess worked more than 15 hours"}' emp.txt 

3 employess worked more than 15 hours

对于第三个字段超过15的每行,emp 的前一个值加1

求和与平均值

如下是一个使用NR来计算薪酬均值的程序:

[root@ localhost ~]# awk '{pay =pay+$2 * $3} END {print NR, "employees";print "total pay is ",pay;print "average pay is ",pay/NR}' emp.txt 
6 employees
total pay is  337.5
average pay is  56.25

处理文本

AWK的优势之一是能像大多数语言处理数学一样方便地处理字符串。AWK变脸可以保存数字也可以保存字符串这个程序会找出时薪最高的员工

[root@ localhost ~]# awk ' $2 > maxrate {maxrate=$2;maxemp=$1} END {print "higtest hourly rate:",maxrate,"for",maxemp}' emp.txt 
higtest hourly rate: 5.50 for Mary

字符按连接

可以合并老字符串来创建新字符串。这种操作称为连接。

[root@ localhost ~]# awk '{name=name $1 " "} END {print name}' emp.txt 
通过将每个名字和一个空格附加到变脸name的前一个值,将来所有员工的姓名手机进单个字符串中。最后END动作打印出name的值。

Beth Dan kathy Mark Mary Susie

AWK程序中,连接操作的表现形式是将字符串值一个接一个地写出来。对于每个输入的行,程序的一个语句先连接三个字符串。name的前一个值,当前的第一个字段以及一个空格,分后将得到的字符串赋值给name,因此,读取所有的输入行之后,name就是个字符串,包含所有员工的姓名。每个姓名后面跟着一个空格。用于保存字符串的变量的默认初始值是空字符串(也就是说该字符串包含零个字符),因此这个程序中的name不需要显示初始化。

打印最后一个输入行
虽然再END动作中的NR还保留着它的值,但是$0没有。

[root@ localhost ~]# awk '{last=$0} END {print last}' emp.txt 
Susie    4.25    18

内置函数

我们已看到AWK提供了内置变量来保存没修频繁使用的数量,比如:字段的数量和输入行的数量,类似地,也有内置函数用来计算其他有用的数值。除了平方根、对数、随机数诸如此类的算术函数,也有操作文本的函数。其中值意是length,计算一个字符串中的数量问题。例如:计算每个人的姓名的长度

[root@ localhost ~]# awk '{print $1 ,length($1)}' emp.txt 
Beth 4
Dan  3
kathy 5
Mark 4
Mary 4
Susie 5

行、单词以及字符的计数

这个程序使用了length、NF、以及NR来统计输入中行、单词以及字符的数量。为了简便,我们将每个字段看作一个单词

[root@ localhost ~]# awk '{nc=nc+length($0)+1;nw=nw+NF} END {print NR,"lines,",nw, "word,", nc, "characters"}' emp.txt 
6 lines, 18 word, 77 characters

$0并不包含每个输入行的末尾的换行符,所以我们要另外加个1.

1.6控制语句

AWK为选择提供了一个if-else语句,以及为循环提供了几个语句,所以都消防C语言中对应的控制语句。他们仅可以在动作中使用。

if-else 语句

如下程序将计算时薪超过6美元的员工的总薪酬与平均薪酬。它使用一个if来防范季孙平均薪酬时的零除问题。

[root@ localhost ~]# awk '$2 > 6 { n = n + 1; pay = pay + $2 * $3 }
> END    { if (n > 0)
>             print n, "employees, total pay is", pay,
>                      "average pay is", pay/n
>          else
>              print "no employees are paid more than $6/hour"
>         }' emp.txt
输出结果
no employees are paid more than $6/hour

if-else语句中,if后的 条件会被计算。如果为真,执行第一个pring语句。否则,执行第二个print语句。注意我们能可以使用一个逗号将一个长语句截断多行来书写。

while语句

一个while语句有一个条件和一个执行体。条件为真时执行体重的语句会被重复执行。这个程序使用公式 value=amount(1+rate)yearsvalre=amount(1+rate)years

来掩饰以特定的利率投资一定量的钱,其数值时如何随着年岁增长的。

# interest1 - 计算复利
#   输入: 钱数    利率    年数
#   输出: 复利值

{   i = 1
    while (i <= $3) {
        printf("\t%.2f\n", $1 * (1 + $2) ^ i)
        i = i + 1
    }
} 

条件是while后括弧包围的表达式;循环体是条件后大括号包围的两个表达式。print规格字符串中的\t代表制表符;^是指数操作符。从#开始至行尾的是文本注释。会被AWK忽略,但能帮助程序的而读者理解程序做的事情。

for语句

另一个语句,for,将大多数循环都包含的初始化,测试、以及自增压缩成一行。如下是之前利息计算的for版本:

# interest1 - 计算复利
#   输入: 钱数    利率    年数
#   输出: 每年末的复利

{ for (i = 1; i <= $3; i = i + 1)
    printf("\t%.2f\n", $1 * (1 + $2) ^ i)
}

初始化i=1 只执行一次,接下来,测试条件i<=$3;如果为真,则执行循环体的printf语句。循环体执行结束后执行自增i=i

+1,接着又另一次条件测试开始下一个循环迭代。代码更加紧凑,由于循环体仅是一条语句,所以不需要大括号来来包围他。

1.7数组

awk为存储一组相关的值提供了数组。虽然数组给予了awk很强的能力,但在这里我们仅展示一个简单的例子。如下程序将按行逆序打印输入。第一个动作将输入行存为数组 line 的连续元素;即第一行放在 line[1] ,第二行放在 line[2] , 依次继续。 END 动作使用一个 while 语句从后往前打印数组中的输入行:

# 反转 - 按行逆序打印输入

    { line[NR] = $0 }  # 记下每个输入行

END { i = NR           # 逆序打印
      while (i > 0) {
        print line[i]
        i = i - 1
      }
    }

以emp.txt 为输入,输出为

Susie    4.25   18
Mary     5.50   22
Mark     5.00   20
Kathy    4.00   10
Dan      3.75   0
Beth     4.00   0

如下是使用for语句是实现的相同示例

# 反转 - 按行逆序打印输入

    { line[NR] = $0 }   # 记下每个输入行

END { for (i = NR; i > 0; i = i - 1)
        print line[i]
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!