[转帖]《AWK程序设计语言》笔记(1)—— AWK入门与简单案例

awk,程序设计,语言,笔记,入门,简单,案例 · 浏览次数 : 0

小编点评

**1. 输入行总行数** ```print NR ``` **2. 打印第 10 行** ```print NR ``` **3. 打印每一个输入行的最后一个字段** ```print $NF ``` **4. 打印最后一行的最后一个字段** ```print field ``` **5. 打印字段数多于 4 个的输入行 NF** ```print NF, $0 ``` **6. 打印最后一个字段值大于 4 的输入行 $NF** ```print $NF > 4 ``` **7. 打印所有输入行的字段数的总和** ```print nf ``` **8. 打印包含 Beth 的行的数量 /Beth/** ```print nlines ``` **9. 打印具有最大值的第一个字段, 以及包含它的行 (假设 $1 总是 正的)** ```print max, maxline ``` **10. 打印至少包含一个字段的行 NF** ```print NF, $0 ``` **11. 打印长度超过 80 个字符的行 length($0) > 80** ```print length($0) > 80 ``` **12. 在每一行的前面加上它的字段数** ```print NF, $0 ``` **13. 打印每一行的第 1 与第 2 个字段, 但顺序相反** ```print $2, $1 ``` **14. 交换每一行的第 1 与第 2 个字段, 并打印该行** ```print temp, $1 ``` **15. 将每一行的第一个字段用行号代替** ```$1 = NR; print ``` **16. 打印删除了第 2 个字段后的行 { $2 = \"\"; print }** ```print $2 = \"\"; print ``` **17. 将每一行的字段按逆序打印** ```for (i = NF; i > 0; i = i - 1) printf(\"%s \", $i)printf(\"\\") ``` **18. 打印每一行的所有字段值之和** ```for (i = 1; i <= NF; i = i + 1) sum = sum + $iprint sum ``` **19. 将所有行的所有字段值累加起来** ```for (i = 1; i <= NF; i = i + 1) sum = sum + $i ``` **20. 将每一行的每一个字段用它的绝对值替换** ```for (i = 1; i < NF; i = i + 1) if ($i < 0) $i = -$iprint ```

正文

原文为 《The AWK Programming Language》,GitHub上有中译版,不过有些内容翻译的比较奇怪,建议跟原版对照着看 https://github.com/wuzhouhui/awk

本篇的小案例基本均基于文件 emp.data,三个字段分别为:员工名、每小时工资工作时长,每一行代表一个雇员的记录

  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

一、 AWK入门

1. 入门小案例

打印每位雇员的名字以及他们的报酬 (每小时工资*工作时长),雇员的工作时长必须大于0。

  1. awk '$3 > 0 { print $1, $2 * $3 }' emp.data
  2. # $n 代表第n列,$0为整行数据
  3. # 过滤 $3>0 的行,输出$1及$2*$3

被单引号包围的部分是一个完整的 awk 程序,一般由 模式动作 (pattern-action) 组成,当然模式可以没有。模式 $3 > 0 扫描每一个输入行, 如果该行的第三列大于0,则执行动作,为每一个匹配行打印第一个字段以及第二与第三个字段的乘积。

想知道哪些员工在偷懒(工作时长为0

awk '$3 == 0 { print $1 }' emp.data

 

2. 运行 AWK 程序

awk 程序有多种运行方式:

  • 读取输入文件  awk 'program' input files输入文件可以有多个,例如
  1. # 按顺序打印文件emp.data,emp.data2符合条件的每一行的第一个字段
  2. awk '$3 == 0 { print $1 }' emp.data emp.data2

  • 读取终端输入内容 awk 'program'awk 会将 program 应用到你在终端输入的内容,直到你输入文件结束标志 (ctrl+d)
awk '$3 == 0 { print $1 }'

橙色部分为输出,其余为输入,不符合条件则无返回结果

  • 将awk程序放入文件,并用-f参数指定  awk -f progfile optional list of files注意progfile中程序不用加''

 

二、 AWK输出

awk 的数据只有两种类型:数值与字符串。emp.data 是很典型的待处理数据,它既有单词也包括数值,且字段之间通过制表符或空格分隔。

1. 打印每一行

{ print }或者{ print $0 },即:

  1. awk '{ print }' emp.data
  2. #or
  3. awk '{ print $0 }' emp.data

 

2. NF,字段的数量

{ print NF, $1, $NF }打印每行字段数、第一个字段、最后一个字段。

awk ' { print NF, $1, $NF } ' emp.data

 

3. NR,打印行号

可以使用 NR $0 emp.data 的每一行加上行号。

awk ' { print NR, $0 } ' emp.data

 

4. 列计算

可以用字段的值进行计算,并将计算得到的结果放在输出语句中,{ print $1, $2 * $3 } 就是最开始的例子。

 

5. 拼文本

可以把单词放在字段与算术表达式之间,例如 { print "total pay for", $1, "is", $2 * $3 }

awk '{ print "total pay for", $1, "is", $2*$3 }' emp.data

 

三、 更精美的输出

格式化输出需要使用 printf 语句,printf 几乎可以产生任何种类的输出

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

format 是一个字符串,它包含按字面打印的文本,中间为格式说明符,格式说明符是%加上后面几个字符,这些字符控制 value 的输出格式。每个格式说明符对应一个value值,因此,格式说明符的数量应该和被打印的 value 一样多。

 

1. 打印每位雇员的报酬

awk ' { printf("total pay for %s is %.2f\n", $1, $2 * $3) } ' emp.data

这个 printf 语句包含两个格式说明符:%s 将第一个值 $1 以字符串的形式打印;%.2f 将第二个值 $2*$3 按照浮点数格式打印且保留两位小数。其他内容按照字面值打印,\n 表示换行符。printf 不会自动产生空格符或换行符,用户必须自己创建。

 

来看另一个打印每位雇员的名字与报酬程序:{ printf("%-8s %6.2f\n", $1, $2 * $3) }

%-8s -代表左对齐输出,8占用 8 个字符的宽度,s代表字符串;6.2f,6代表至少占用 6 个字符的宽度,.2f跟前例相同。

awk '{ printf ("%-8s %6.2f\n",$1,$2*$3) }' emp.data

 

为每一位雇员打印所有的数据,包括报酬,报酬按照升序排列。最简单的办法是使用awk 在每一位雇员的记录前加上报酬,然后 awk 的输出通过管道传递给 sort 命令。

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

 

四、 选择(过滤)

1. 数值选择

比如过滤报酬大于50的员工信息,模式部分应该是 $2 * $3 > 50,结合前面的格式化:

awk ' $2 * $3 > 50 { printf("%.2f for %s\n"),$2*$3,$1 }' emp.data

 

2. 通过文本内容选择

也可以选择那些包含特定单词或短语的输入行,下面程序打印所有第一个字段是Susie 的行:

awk ' $1 == "Susie" { printf("%.2f for %s\n"),$2*$3,$1 }' emp.data

也可以通过正则表达式完成,下面程序打印所有包含 Susie 的行:

awk ' /Susie/ { printf("%.2f for %s\n"),$2*$3,$1 }' emp.data

 

3. 模式的组合

模式可以使用逻辑运算符(&&, ||, !,)进行组合,例如 $2 >= 4 || $3 >= 20

  1. awk ' $2 >= 4 || $3 >= 20 { printf("%.2f for %s\n"),$2*$3,$1 }' emp.data
  2. # or
  3. awk ' !($2 < 4 && $3 < 20) { printf("%.2f for %s\n"),$2*$3,$1 }' emp.data

这两个程序是逻辑上是等价的,但第二个可读性比较差

 

五、 BEGIN END

有两个特殊的模式 BEGIN 和 END。BEGIN 在第一个输入文件的第一行之前被匹配,END 在最后一个输入文件的最后一行被处理之后匹配。下面程序使用 BEGIN 打印一个标题:

awk 'BEGIN { print"NAME RATE HOURS"; print "" } { print } ' emp.data

BEGIN {}构造标题,{ print }输出文件内容,相当于

awk 'BEGIN { print"NAME RATE HOURS"; print "" } ' emp.data; cat emp.data

BEGIN 与END 分别提供了一种控制初始化与扫尾的方式,BEGIN 与 END 不能与其他模式作组合。如果有多个 BEGIN,会按照它们在程序中出现的顺序执行,多个 END 同样适用。

BEGIN 的一个常见用途是更改输入行的分隔符,分隔符由内建变量FS 控制,默认由空格分割(FS=' ')。将 FS设置为什么值,就会使其成为字段分隔符。

下面程序在 BEGIN 里将字段分隔符设置为 \t,并在输出之前打印标题。第二个 printf 语句将输出格式化成一张表格,使得每一列都刚好与标题的列表头对齐,END 打印总和。

  1. BEGIN { FS = "\t" # make tab the field separator
  2. printf("%10s %6s %5s %s\n\n",
  3. "COUNTRY", "AREA", "POP", "CONTINENT")
  4. }
  5. { printf("%10s %6d %5d %s\n", $1, $2, $3, $4)
  6. area = area + $2
  7. pop = pop + $3
  8. }
  9. END { printf("\n%10s %6d %5d\n", "TOTAL", area, pop) }

 

六、  AWK 计算

1. count

下面程序计算工作时长超过 15 个小时的员工人数

  1. awk ' $3 > 15 { emp = emp + 1 }
  2. END { print emp, "employees worked more than 15 hours" } ' emp.data

awk 中,用户变量不需要事先声明,数值默认初始化为0。对每个第三列>15的行,对emp加1,统计结束后利用END最后输出结果。

 

2. 求总和与平均数

为了计算雇员的人数,可以使用内建变量 NR(当前行数),当所有输入都处理完毕时,它的值就是读取到的行数。

  1. # 计算平均报酬
  2. awk ' { pay = pay + $2*$3 }
  3. END { print NR, "employees"
  4. print "total pay is", pay
  5. print "average pay is", pay / NR
  6. } ' emp.data

显然如果NR 的值为0,程序会报错,后面再看如何处理。

 

七、 操作文本

Awk 的长处之一是它可以非常方便地操作字符串。

1. 搜索每小时工资最高的雇员

  1. awk ' $2 > maxrate { maxrate = $2; maxemp = $1 }
  2. END { print "highest hourly rate:",maxrate, "for", maxemp } ' emp.data

在这个程序里,变量 maxrate 保存的是数值,maxemp 保存的是字符串,如果有多个雇员都拥有相同的最高每小时工资,这个程序只会打印第一个人的名字。

 

2. 字符串拼接

  1. awk ' { name = name $1 " " }
  2. END { print name } ' emp.data

将所有雇员名存储到一个单独的字符串中,每一次拼接都是把名字与一个空格符添加到变量 name 的值的末尾,最后在 END 动作中被打印出来。存储字符串的变量的初始值默认为空字符串因此 name 不需要显式地初始化。

 

3. 打印最后一行

END  NR 的值会被保留下来,但是 $0 不会,需要通过用户变量存储。

  1. awk ' { last = $0 }
  2. END { print last } ' emp.data

如果在END前面执行,print $0会打印每行的值,如果放在END里,由于已经遍历完了,就只打印最后一行。

 

4. 内建函数

awk 提供一些内建函数,例如求平方根、取对数、随机数,还有用来操作文本的函数,比如 length 用来计算字符串中字符的个数。下面程序计算每个名字的长度:

awk ' { print $1, length($1) } ' emp.data

 

八、 流程控制语句

Awk 提供了 if-else 语句以及循环语句 while和for,这些都来源于 C 语言,只能用在Action部分。


1. if-else 语句

下面程序计算每小时工资>6 的雇员的总报酬与平均报酬。在计算平均数时,它用到了 if 语句,避免用 0 作除数。

  1. awk ' $2 > 6 { emp_num = emp_num + 1; pay = pay + $2*$3 }
  2. END { if (emp_num > 0)
  3. print emp_num,"employees, total pay is", pay,"avg pay is", pay/emp_num
  4. else
  5. print "no employees are paid more than $6/hour"
  6. } ' emp.data

改为判断每小时工资>的雇员

 

2. while 语句

while 含有一个条件判断与一个循环体。当条件为真时,执行循环体。

下面程序展示了一笔钱在特定的利率下如何随着投资时间的增长而增加,假设计算的公式是

  1. { i = 1
  2. while ( i <= $3 ) {
  3. printf("\t%.2f\n",$1*(1+$2)^i )
  4. i = i + 1
  5. }
  6. }
  7. # \t 表示一个制表符; ^ 是指数运算符

 

3. for 语句

大多数循环都包括初始化、判断、递增值,for 语句将这三者压缩成一行。

这里是前一个计算投资回报的程序, 不过这次用 for 循环:

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

 

九、 数组

awk 提供数组用来存储一组相关的值数组功能强大,,但是我们在这里只展示一个简单的例子。

下面程序按行逆序显示输入数据

  1. # reverse - print input in reverse order by line
  2. { line[NR] = $0 } # 顺序记录每行内容
  3. END { i = NR # 逆序输出
  4. while (i > 0) {
  5. print line[i]
  6. i = i - 1
  7. }
  8. }

for 循环实现的等价的程序

  1. # reverse - print input in reverse order by line
  2. { line[NR] = $0 }
  3. END { for (i = NR; i > 0; i = i - 1)
  4. print line[i]
  5. }

 

十、 实用 一行手册

虽然 awk 可以写出非常复杂的程序,,但是许多实用的程序并不比我们目前为止看到的复杂多少。这里有一些小程序集合对读者应该会有一些参考价值,大多数是我们已经讨论过的程序的变形。

1. 输入行的总行数

END { print NR }

2. 打印第 10

 

NR == 10


3. 打印每一个输入行的最后一个字段

{ print $NF }


4. 打印最后一行的最后一个字段

  1. { field = $NF }
  2. END { print field }

 

5. 打印字段数多于 4 个的输入行

NF > 4


6. 打印最后一个字段值大于 4 的输入行

$NF > 4


7. 打印所有输入行的字段数的总和

  1. { nf = nf + NF }
  2. END { print nf }


8. 打印包含 Beth 的行的数量

  1. /Beth/ { nlines = nlines + 1 }
  2. END { print nlines }


9. 打印具有最大值的第一个字段, 以及包含它的行 (假设 $1 总是 正的) 18

  1. $1 > max { max = $1; maxline = $0 }
  2. END { print max, maxline }


10. 打印至少包含一个字段的行

NF > 0


11. 打印长度超过 80 个字符的行

length($0) > 80


12. 在每一行的前面加上它的字段数

{ print NF, $0 }


13. 打印每一行的第 1 与第 2 个字段, 但顺序相反

{ print $2, $1 }


14. 交换每一行的第 1 与第 2 个字段, 并打印该行

{ temp = $1; $1 = $2; $2 = temp; print }


15. 将每一行的第一个字段用行号代替

{ $1 = NR; print }


16. 打印删除了第 2 个字段后的行

{ $2 = ""; print }


17. 将每一行的字段按逆序打印

  1. { for (i = NF; i > 0; i = i - 1) printf("%s ", $i)
  2. printf("\n")
  3. }


18. 打印每一行的所有字段值之和

  1. { sum = 0
  2. for (i = 1; i <= NF; i = i + 1) sum = sum + $i
  3. print sum
  4. }


19. 将所有行的所有字段值累加起来

  1. { for (i = 1; i <= NF; i = i + 1) sum = sum + $i }
  2. END { print sum }


20. 将每一行的每一个字段用它的绝对值替换

  1. { for (i = 1; i <= NF; i = i + 1) if ($i < 0) $i = -$i
  2. print}

与[转帖]《AWK程序设计语言》笔记(1)—— AWK入门与简单案例相似的内容:

[转帖]《AWK程序设计语言》笔记(1)—— AWK入门与简单案例

原文为 《The AWK Programming Language》,GitHub上有中译版,不过有些内容翻译的比较奇怪,建议跟原版对照着看 https://github.com/wuzhouhui/awk 本篇的小案例基本均基于文件 emp.data,三个字段分别为:员工名、每小时工资、工作时长,

[转帖]Linux系统awk命令详解

AWK 是一种处理文本文件的语言,是一个强大的文本分析工具。 之所以叫 AWK 是因为其取了三位创始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符。 实际上 AWK 的确拥有自己的语言: AWK程序设计语言,三位

[转帖] q命令-用SQL分析文本文件

https://www.cnblogs.com/codelogs/p/16060830.html 简介# 在Linux上分析文本文件时,一般会使用到grep、sed、awk、sort、uniq等命令,但这些命令都有一定的学习成本,而如果是用SQL来分析数据的话,这对广大后端程序员来说,就要简单很多了

[转帖]awk提取某一行某一列的数据

https://www.jianshu.com/p/dbcb7fe2da56 1、提取文件中第1列数据 awk '{print $1}' filename > out.txt 2、提取前2列的文件 awk `{print $1,$2}' filename > out.txt 3、打印完第一列,然后打

[转帖]awk 中 FS的用法

https://www.cnblogs.com/rohens-hbg/p/5510890.html 在openwrt文件 ar71xx.sh中 查询设备类型时,有这么一句, machine=$(awk 'BEGIN{FS="[ \t]+:[ \t]"} /machine/ {print $2}' /

[转帖]awk 文本处理

https://juejin.cn/post/6844903860629143559 awk 文本处理 awk 是一种样式扫描和处理语言,使用 Linux 的 awk 命令可以高效快捷地进行文本处理。awk 扫描文本的每一行并执行指定的命令。 awk 诞生于 1977 年,借鉴了 C 语言等编程语言

[转帖]awk——getline

http://t.zoukankan.com/panscience-p-4685698.html A.getline从整体上来说,应这么理解它的用法: 当其左右无重定向符 | 或 < 时,getline作用于当前文件,读入当前文件的第一行给其后跟的变量var 或$0(无变量);应该注意到,由于awk

[转帖]awk(五) 字符串函数(substr截取)

https://www.jianshu.com/p/bee599fabe85 1. substr(s,p) 返回字符串s中从p开始到最后部分 每一行从第101个字符开始截取到末尾awk '{print substr($0, 101)}' input.file 2. substr(s,p,n) 返回字

[转帖]awk(四) 字符串函数(gsub替换)

https://www.jianshu.com/p/d465f4dccbbe 1. gsub(r,s) 在整个$0中用s替代r 删除文件中所有逗号awk 'gsub(",", ""){print}' input.file# 注意gsub内部用双引号"" 2. gsub(r,s,t) 在整个t中用s替

[转帖]awk(三) 列运算

https://www.jianshu.com/p/b46f783832e3 1. 求每一行中指定列的最大/小值 awk '{m=0;for(x=5;x<=NF;x++)if($x>m) {m=$x};print m}' input.file # 求第5列到最后一列中每行的最大值 awk '{m=0