awk使用笔记

本文阅读量 Posted by Kird on 2020-10-16

awk使用笔记

编者按

本篇为编者阅读博客[笔记] The AWK Programming Language(ADDISON-WESLEY, 1988)做的笔记,跟着博客把awk编程相关的内容进行梳理以及少数地方的勘误,如果有需要请阅读原文。

1.AWK 入门教程(AN AWK TUTORIAL)

1.1 Getting Started

Awk 程序一般都很简短,只有一两行。通用格式为:

1
awk 'patterns { actions }' files
  • pattern:用于过滤出匹配的行(lines matched by any of the patterns)
  • action:对匹配的行执行的动作

举例文件为:

1
2
3
4
5
6
7
$ cat emp.data
Beth 4.00 0
Dan 3.75 0
Kathy 4.00 10
Mark 5.00 20
Mary 5.50 22
Susie 4.25 18

三列分别表示:员工姓名,时薪,工作时长。下面的程序计算工时非零的员工的工资:

1
awk '$3 > 0 {print $1, $2 * $3}' emp.data
  • $3 > 0为action,匹配第3列大于0的行
  • {print $1, $2 * $3} 为action,打印姓名,时薪x工时
  • '$3 > 0 {print $1, $2 * $3}',单引号里整个为awk程序,通常awk程序较简短,如果awk程序较长,可以把内容放到文件中,通过-f选项进行读取处理。

如下就是使用-f选项进行读取处理:

1
2
3
4
5
6
7
$ cat start.awk
$3>0 {print $1,$2*$3}
$ awk -f start.awk emp.data
Kathy 40
Mark 100
Mary 121
Susie 76.5

AWK 程序的结构

1
2
3
pattern { action }
pattern { action }
...

pattern 和 action 都是可选的(optional),因此{} 将 action 扩起来,以 便与 pattern 区分开。特别地,

  • 如果 pattern 为空:例如 { print $1 },则对所有行执行 action

    1
    2
    3
    4
    5
    6
    7
    $ awk '{print $0}' emp.data
    Beth 4.00 0
    Dan 3.75 0
    Kathy 4.00 10
    Mark 5.00 20
    Mary 5.50 22
    Susie 4.25 18
  • 如果 action 为空:例如 $3 == 0,则打印匹配的行(默认 action)

    1
    2
    3
    $ awk '$3 == 0' emp.data
    Beth 4.00 0
    Dan 3.75 0

1.2 简单输出(Simple Output)

Awk 中只有两种数据类型

  1. 数字(number)
  2. 字符串(strings of characters.)

Awk 读取每行,然后以空格或 tab 将行分割成多个字段(列),其中:

  • $1$2$3:分别表示第 1、2、3 个字段(列)
  • $0:该行(the entire line)
  • NF:字段数量(Number of Fields),即列数
  • $NF$(NF-1):最后一个字段(最后一列)、倒数第二个字段(列)
  • NR: 行数数量(Number of Rows),即当前行数
  • FNR:和NR功能类似,awk每打开一个文件,FNR即重置为0,NR则继续累加。对单个文件操作,功能一样。

例子

  • 打印整行:{ print }{ print $0 }
  • 打印第 1 和 3 列:{ print $1, $3 }
  • 打印列数、第 1 列、最后一列、倒数第 2 列:{ print NF, $1, $NF, $(NF-1) }
  • 打印第 1 列、第 2、3 列之积:{ print $1, $2 * $3 }
  • 打印行并加上行号:{ print NR, $0 }

1.3 高级输出(Fancier Output)

printf格式化输出

printf 能够输出几乎全部格式,格式为:printf (format, value1 , value2 , ... , value3,...)

举例:

1
2
3
4
5
6
7
$ awk '{ printf("total pay for %s is $%.3f\n", $1, $2 * $3) }' emp.data
total pay for Beth is $0.000
total pay for Dan is $0.000
total pay for Kathy is $40.000
total pay for Mark is $100.000
total pay for Mary is $121.000
total pay for Susie is $76.500

还有一个print输出,区别为**print 会自动加换行符,而 printf 不会**。

如上面的例子使用print输出:

1
2
3
4
5
6
7
$ awk '{ print("total pay for "$1" is  "$2*$3) }' emp.data
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

排序输出

使用sort对结果进行排序即可

1.4 选择/过滤行(Selection)

通用选择行

  • 列值过滤:awk '$2 >= 5' emp.data
  • 表达式计算结果过滤:awk '$2 * $3 > 50 { printf("$%.2f for %s\n", $2 * $3, $1) }' emp.data
  • 字符串匹配:awk '$1 == "Susie" { printf("$%.2f for %s\n", $2 * $3, $1) }' emp.data
  • 正则:awk '/Susie/ { printf("$%.2f for %s\n", $2 * $3, $1) }' emp.data
  • 其他组合:$2 >= 4 || $3 >= 20!($2 < 4 && $3 < 20)NF != 3

特殊选择BEGIN/END

  • BEGIN:在第一行之前匹配(即,在程序开始时执行且只执行一次
  • END:在最后一行之后匹配(即,在程序结束之前执行且只执行一次
1
2
3
4
5
6
7
8
$ awk 'BEGIN { print "NAME  RATE HOURS" } $3>=0 ' emp.data
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

1.5 计算(Computing with AWK)

统计数量:工时为0的员工人数

1
2
$ awk ' $3==0 {emp+=1} END {printf("%d employees worked 0 hours\n",emp)}' emp.data
2 employees worked 0 hours

数值类型的变量默认初始化为 0,因此不需要自己初始化 emp 变量。

求和、求平均

1
2
$ awk '{total+=$2*$3} END {printf("total pay is %.2f,average pay is %.2f\n",total,total/NR)}' emp.data
total pay is 337.50,average pay is 56.25

注意,上面出现的action均是一个,如果多个action可以通过分号或者换行隔开,如上例:

1
2
3
4
5
6
7
8
9
10
$ awk '{total+=$2*$3;print "test more 1 action"} END {printf("total pay is %.2f,average pay is %.2f\n",total,total/NR);print "test more 1 action";print "test more 1 action"}' emp.data
test more 1 action
test more 1 action
test more 1 action
test more 1 action
test more 1 action
test more 1 action
total pay is 337.50,average pay is 56.25
test more 1 action
test more 1 action

处理文本:打印时薪最高的员工信息($2最大)

1
2
$ awk '$2>max {max=$2;emp=$1} END {printf "%s gets the max %.3f\n",emp,max}' emp.data
Mary gets the max 5.500

字符串拼接(concatenation):在一行内打印所有员工名

1
2
$ awk '{all=all$1" "} END {print all}' emp.data
Beth Dan Kathy Mark Mary Susie

打印最后一行

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

内置函数(Built-in Functions)

1
2
3
4
5
6
7
$ awk '{print $1,length($1)}' emp.data
Beth 4
Dan 3
Kathy 5
Mark 4
Mary 4
Susie 5

统计行数、单词数、字符数

1
2
3
4
5
6
7
8
9
10
$ cat count2.awk
{ nc=nc+length($0)+1
nw=nw+NF
}
END{
print NR, "lines", nw, "words,", nc, "characters"
}

$ awk -f count2.awk emp.data
6 lines 18 words, 82 characters

1.6 控制流 Statements

和其他编程语言一样,awk中也有相关控制语句。

if-else

1
2
3
4
5
6
7
8
9
10
$ cat if.awk
$2 > 4 { n += 1 ; pay = pay + $2 * $3 }
END { if (n > 0)
print n, "employees(that more than 4RMB/hour) total pay is", pay,
"average pay is", pay/n
else
print "no employees are paid more than 4RMB/hour"
}
$ awk -f if.awk emp.data
3 employees(that more than 4RMB/hour) total pay is 297.5 average pay is 99.1667

注意到逗号可以将较长的行分为多行

while

举例:计算复利

  • 输入格式:本金 利率 年限,如 1000 0.1 2

  • 输出格式:每年的本金和利息之和,输出

    1
    2
    1100.00
    1210.00

awk程序为:

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

for

如上例,计算复利,输入格式:本金 利率 年限,如 1000 0.1 2

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

1.7 数组

awk行倒序打印文本

使用数组保存每一行内容:

while循环倒序打印数组,

1
2
3
4
5
6
7
$ awk '{tmp[NR]=$0}END{i=NR;while(i>0){print tmp[i--]}}' emp.data
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循环倒序打印数组,

1
2
3
4
5
6
7
$ awk '{tmp[NR]=$0}END{for (i=NR;i>0;i--) print tmp[i]}' emp.data
Susie 4.25 18
Mary 5.50 22
Mark 5.00 20
Kathy 4.00 10
Dan 3.75 0
Beth 4.00 0

1.8 实用单行命令

编号 功能 AWK 程序 类似效果的命令
1 打印总行数 END { print NR } cat <file> | wc -l
2 打印第 10 行 NR == 10 head -n10 <file> | tail -n1
3 打印最后一列 { print $NF }
4 打印最后一行的最后一列 { f = $NF } END { print f }END {print $NF}
5 打印有 4 列以上的行 NF > 4
6 打印最后一列的值大于 4 的行 $NF > 4
7 打印所有输入的总字段数 { nf += NF } END { print nf }
8 打印包含 Beth 的总行数 /Beth/ { n++ } END { print n }
9 打印第 1 列的最大值及对应的行(假设第 1 列为正) $1 > max { max = $1; line = $0 } END { print max, line }
10 打印列数大于 1 的行 NF > 1
11 打印长度大于 80 的行 length($0) > 80
12 打印每行的列数,及该行 { print NF, $0 }
13 打印第 2, 1 列 { print $2, $1 }
14 交换第 1, 2 列,打印全部行 { t = $1; $1 = $2; $2 = t; print } #勘误:原博客语句使用’'~",错误
15 第 1 列换成行号,打印全部行 { $1 = NR; print }
16 去掉第 2 列,打印全部行 { $2 = ""; print }
17 列倒序,打印全部行 '{ for(i=NF; i>0; i--) printf("%s ", $i); printf("\n");} '
18 打印每行的和 '{ sum=0; for(i=1; i<=NF; i++) sum += $i; print sum }'
19 打印所有行的和 '{ for(i=1; i<=NF; i++) sum += $i; } END { print sum }'
20 所有字段取绝对值,打印全部行 '{ for(i=1; i<=NF; i++) if ($i<0) $i = -$i; print }'

2. AWK 编程语言(THE AWK LANGUAGE)

AWK 工作流程:

  1. 语法检查
  2. 按行读取输入
  3. 针对每行分别匹配 pattern,然后执行对应的 action
    • 如果 pattern 为空,匹配所有行
    • 如果 action 为空,打印匹配的行

本章将用下面的输入作为例子:

1
2
3
4
5
6
7
8
9
10
11
12
$ cat countries.txt
USSR 8642 275 Asia
Canada 3852 25 North America
China 3705 1032 Asia
USA 3615 237 North America
Brazil 3286 134 South America
India 1267 746 Asia
Mexico 762 78 North America
France 211 55 Europe
Japan 144 120 Asia
Germany 96 61 Europe
England 94 56 Europe

这个输入的特别之处

  1. 列之间用 tab 分隔(\t
  2. 最后一列有空格(North AmericaNorth America

AWK 程序格式

  • 同一个 statement 太长需要换行时,用单斜杠(\)连接
  • 多个 statement 可以写到同一行,用分号(;)分开
  • 注释以 # 开头
  • 空格和 tab 会被忽略,因此可以适当添加空格/空行提高程序可读性

例子:

1
2
3
4
5
{ print \
$1, # country name
$2, # area in thousands of square miles
$3 # population in millions
}

2.1 Patterns

6 种 pattern:

  • BEGIN { statements }`:程序开始时(读取任何输入之前)执行一次

  • END { statements }`:程序结束时(读完所有输入之后)执行一次

  • expression { statements }expression` 为真时执行

  • /regular expression/ { statements }`:匹配到正则表达式时执行

  • compound pattern { statements }:复合表达式(包含||&&` 等逻辑)为真时执行

  • pattern1 , pattern2 { statements }:**匹配到pattern1时开始对接下来的每一行执行 actions,匹配到pattern2` 时停止对后面的行执行**

BEGIN 和 END

  • BEGINEND 模式的 action 不能为空。
  • BEGINEND 不能与其他 pattern 混用。
  • 可以有多个 BEGIN,按顺序执行END 同理。
  • BEGINEND 顺序没关系,总是 BEGIN 先执行。

BEGIN 的一个用处是:在开始处理输入之前设置字段分隔符FS,Field Separator)。 默认的 FS 是空格和 tab

BEGIN 还用来打印表头等信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ cat fs.awk
BEGIN { FS = "\t" # make tab the field separator
printf("%10s %6s %5s %s\n\n", "COUNTRY", "AREA", "POP", "CONTINENT")
}
{ printf("%10s %6d %5d %s\n", $1, $2, $3, $4)
area = area + $2
pop = pop + $3
}
END { printf( "\n%10s %6d %5d\n", "TOTAL", area, pop) }
$ awk -f fs.awk countries.txt
COUNTRY AREA POP CONTINENT

USSR 8642 275 Asia
Canada 3852 25 North America
China 3705 1032 Asia
USA 3615 237 North America
Brazil 3286 134 South America
India 1267 746 Asia
Mexico 762 78 North America
France 211 55 Europe
Japan 144 120 Asia
Germany 96 61 Europe
England 94 56 Europe

TOTAL 25674 2819

FS也可以通过命令行选项-F'\t'指定。

Expressions as Patterns

AWK 中,任何类型的操作符都可以处理任何类型的数据

  1. 数字会被自动转换成字符串
  2. 字符串也会被自动转换成数字

比较操作符一共有 8 个:

  1. <
  2. <=
  3. ==
  4. !=
  5. >=
  6. >
  7. ~:匹配(matched by)
  8. !~不匹配(not matched by)

字符串比较:

  1. “Canada” < “China”
  2. “Asia” < “Asian”
  3. $0 >= "M"选择首字母的 ASCII 码大于 M 的行
1
2
$ awk 'NR==2' countries.txt
Canada 3852 25 North America

String-Matching Patterns

类型:

  1. /regexpr/:对整行进行匹配,等价于 $0 ~ /regxpr/
  2. expression ~ /regexpr/:对 expression 进行匹配
  3. expression !~ /regexpr/:反匹配

Regular Expressions

1
2
$ awk '/.*ndia/' countries.txt
India 1267 746 Asia

Compound Patterns

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ awk 'NR==1||NR==2' countries.txt
USSR 8642 275 Asia
Canada 3852 25 North America

$ awk 'NR==1&&NR==2' countries.txt

$ awk 'NR==1&&NF==2' countries.txt

$ awk 'NR==1&&NF==4' countries.txt
USSR 8642 275 Asia

$ awk 'NR!=1 {print NR,$0}' countries.txt
2 Canada 3852 25 North America
3 China 3705 1032 Asia
4 USA 3615 237 North America
5 Brazil 3286 134 South America
6 India 1267 746 Asia
7 Mexico 762 78 North America
8 France 211 55 Europe
9 Japan 144 120 Asia
10 Germany 96 61 Europe
11 England 94 56 Europe

Range Patterns

格式:pattern1, pattern2,表示:

  1. 匹配到 pattern1 时开始对该行及后面的行执行 action
  2. 匹配到 pattern2 时,终止对后面的行执行 action(对当前行还是会执行)
1
2
3
4
5
6
7
8
9
10
$ awk 'NR==2,NR==4 {print NR,$0}' countries.txt
2 Canada 3852 25 North America
3 China 3705 1032 Asia
4 USA 3615 237 North America

$ awk '/India/,/Japan/ {print NR,$0}' countries.txt
6 India 1267 746 Asia
7 Mexico 762 78 North America
8 France 211 55 Europe
9 Japan 144 120 Asia

2.2 Actions

内置变量

record 和 line 的关系说明:

  • 默认情况下,record 分隔符(RS)是换行符,因此一个行就是一个 record。
  • 如果显式修改 RS,也可以让多个行对应一个 record(multiline record),后面会介绍到。

如无特殊说明,本文中 line 和 record 是等价的。

变量 解释 默认值
ARGC 命令行参数个数
ARGV 命令行参数列表
FILENAME 当前文件的文件名
FNR record number in current file
FS 字段分隔符(field separator) 空格或 tab
NF 当前行的字段数
NR 已经读取的记录数(number of records)
OFMT output format for numbers "%.6g"
OFS 输出字段分隔符(output field separator) " "
ORS 输出记录分隔符(output record separator) \n
RLENGTH length of string matched by match function
RS 输入记录分隔符(input record separator) \n
RSTART start of string matched by match function
SUBSEP subscript separator \034
  • 每次读取一个新记录后,会设置 FNR, NF 和 NR

  • $0 发生改变,或者创建了新的字段后,NF 会重置

    1
    2
    3
    4
    $ awk 'FNR==1 {print $0,"NF="NF;$2=10;print $0" new NF="NF}' emp.d
    ata
    Beth 4.00 0 NF=3
    Beth 10 0 new NF=3
  • 执行 match 函数后,RLENGTH and RSTART 会被重新赋值

字段变量(Field Variables)

例子,设置分隔符,并替换第 4 列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ cat replace.awk
BEGIN { FS = OFS = "\t" }
$4 == "North America" { $4 = "NA" }
$4 == "South America" { $4 = "SA" }
{ print }

$ awk -f replace.awk countries.txt
USSR 8642 275 Asia
Canada 3852 25 NA
China 3705 1032 Asia
USA 3615 237 NA
Brazil 3286 134 SA
India 1267 746 Asia
Mexico 762 78 NA
...
  • 如果 $0 发生变化,$1, $2 和 NF 等会被重新计算
  • 如果 $1$2 等等发生变化,$0 也会被重新构建,构建时使用 OFS 作为字段分隔符

不存在的字段:

  • 如果访问不存在的字段,例如 $(NF+1),得到的是空字符串。
  • 给一个不存在的字段赋值,就会创建该字段,例如 $5 = 1000 * $3 / $2

每行的字段数量可能不相同,但有一个最大字段限制,一般是 100。

内置字符串函数

  1. gsub(r, s):在当前行($0)中进行字符串替换,等价于 gsub(r, s, $0)

    1
    2
    3
    4
    $ awk 'gsub("a","x")' <<< "aa bb cc"
    xx bb cc
    $ awk 'gsub(/a/,"x")' <<< "aa ab cc"
    xx xb cc
  2. gsub(r, s, t):在字符串 t 中进行替换

    1
    2
    $ awk 'gsub("a","x",$2)' <<< "aa ab cc"
    aa xb cc
  3. index(s, t):寻找子字符串出现的位置

  4. length(s):字符串长度

  5. match(s, r):匹配字符串,会设置 RSTARTRLENGTH

  6. split(s, a):将字符串 s 分隔为数组 a,使用默认分隔符(FS)

  7. split(s, a, fs):将字符串 s 分隔为数组 a,使用指定分隔符

  8. sprintf():格式化字生成符串

  9. sub(r, s):字符串替换(leftmost),等价于 sub(r, s, t)

  10. sub(r, s, t):字符串替换(leftmost)

  11. substr(s, p):返回从位置 p 开始到最后的子字符串(即 suffix)

  12. substr(s, p, n):返回从位置 p 开始,长度为 n 的子字符串

    1
    2
    $ awk '{$1 =substr($1, 1, 3); print $0 }' <<< '12345 12345'
    123 12345

在替换函数中,& 字符是一个变量,表示匹配到的字符串,来看下面的例子:

1
gsub(/a/, "&b&", "banana")

等价于

1
gsub(/a/, "aba", "banana")

字符串和数字类型互相转换:

  • number "":将数字转换成字符串
  • string + 0:将字符串转换成数字

因此,对不同类型的变量可以这样做转换和比较:

  • $1 "" == $2
  • $1 + 0 == $2 + 0

控制流

关键字:

  • next:开始下一次主输入循环(main input loop),即,开始处理下一行

    1
    2
    3
    4
    5
    6
    $ awk '{if (FNR==2) next;else print NR,$0}' emp.data
    1 Beth 4.00 0
    3 Kathy 4.00 10
    4 Mark 5.00 20
    5 Mary 5.50 22
    6 Susie 4.25 18
  • exit [<expr>]:立即跳转到 END 部分;如果已经在 END 部分,立即退出程序;将 expr 的执行结果作为返回值。

1
2
3
$ awk '{if (FNR==2) exit; else print NR,$0;} END {print "END"}' emp.data
1 Beth 4.00 0
END

数组

AWK 提供了一维数组。数组不需要提前声明,也没有容量大小。

例子,行倒序打印:{ x[NR] = $0 } END { for (i=NR; i>0; i--) print x[i] }

AWK 中的数组是用字符串索引的,因此也叫关联数组(associative arrays)。

例子,分别计算 Asia 和 Europe 的总人口:

1
2
/Asia/   { pop["Asia"] += $3 }
/Europe/ { pop["Europe"] += $3 }

例子,分别为所有地区(第 4 列是地区)计算总人口:

1
2
3
BEGIN { FS = "\t" }
{ pop[$4] += $3 }
END { for (name in pop) print name, pop[name] }

判断 key 是否存在if ("Africa" in pop) ...

从数组中删除一个元素的操作

1
delete array[subscript]

例子:for (i in pop) delete pop[i]

Split 到数组

split("7/4/76", arr, "/") 得到的数组 arr:[7,4,76]

举例,以数组形式打印第一行:

1
2
3
4
5
$ awk 'FNR==1 {split($0, arr);for (i=1;i<=NF;i++) print i,arr[i]}' countries.txt
1 USSR
2 8642
3 275
4 Asia

数组是用字符串来索引的,这可能有点反直觉。但由于 1 的字符串形式是 "1",因此 自动类型转换之后,arr[1] == arr["1"]

多维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ awk 'BEGIN {for (i=1;i<5;i++) for (j=1;j<5;j++) arr[i,j]=0;} END {for (i=1;i<5;i++) for (j=1;j<5;j++) printf("[%s,%s]=%s\n",i,j,arr[i,j]);}' <<< ''
[1,1]=0
[1,2]=0
[1,3]=0
[1,4]=0
[2,1]=0
[2,2]=0
[2,3]=0
[2,4]=0
[3,1]=0
[3,2]=0
[3,3]=0
[3,4]=0
[4,1]=0
[4,2]=0
[4,3]=0
[4,4]=0

2.3 User-Defined Functions

函数格式:

1
2
3
function name(parameter-list) {
statements
}

函数定义可以出现在任何位置。

例子:递归函数调用:

1
2
3
4
5
{ print max(S1, max(S2, S3)) } # print maximum of $1, $2, $3

function max(m, n) {
return m > n ? m : n
}

函数的参数:

  1. 非数组按值传递,传递的是值的复制
  2. 数组按引用传递,能改变数组内的值

2.4 OutPut

  1. print:等价于 print $0
  2. print expr, expr, ...:打印多个表达式的值,之间用 OFS 分隔,最后以 ORS 结束
  3. print expr, expr, ... > <file>
  4. print expr, expr, ... >> <file>
  5. print expr, expr, ... | other_command:重定向到其他命令的标准输入
  6. close(filename)close(command)
  7. system(command)

Output Separators

内置变量:

  • OFS(Output Field Separator):默认是单个空格
  • ORS(Output Record Separator):默认是单个换行符(\n

举例:每行最后增加个"-":

1
2
3
4
5
6
7
8
9
10
11
12
$ awk 'BEGIN{ORS="-\n"} {print}' countries.txt
USSR 8642 275 Asia-
Canada 3852 25 North America-
China 3705 1032 Asia-
USA 3615 237 North America-
Brazil 3286 134 South America-
India 1267 746 Asia-
Mexico 762 78 North America-
France 211 55 Europe-
Japan 144 120 Asia-
Germany 96 61 Europe-
England 94 56 Europe-

Output to file

1
2
3
4
5
{ print($1, $3) > ($3 > 100 ? "bigpop" : "smallpop") }

{ print > $1 }

{ print $1, ($2 > $3) }

Output to Pipes

1
2
3
4
5
BEGIN { FS = "\t" }
{ pop[$4] += $3 }
END { for (c in pop)
printf("%15s\t%6d\n", c, pop[c]) | "sort -t'\t' +1rn"
}

Closing Flies and Pipes

1
close("sort -t'\t' +1rn")

close is necessary if you intend to write a file, then read it later in the same program.

3. 几个awk程序赏析

打印TCP状态

1
2
3
4
$ ss -an |awk '/^tcp/ {state[$2]++} END {for (i in state) print i,state[i]}'
LISTEN 12
ESTAB 5
TIME-WAIT 2

监控CPU使用异常并触发sysrq打印进程记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/sh

while [ 1 ]; do
top -bn2 | grep "Cpu(s)" | tail -1 | awk '{
# $2 is usr, $4 is sys.
if ($2 < 30.0 && $4 > 15.0) {
# save the current usr and sys into a tmp file
while ("date" | getline date) {
split(date, str, " ");
prefix=sprintf("%s_%s_%s_%s", str[2],str[3], str[4], str[5]);
}

sys_usr_file=sprintf("/tmp/%s_info.highsys", prefix);
print $2 > sys_usr_file;
print $4 >> sys_usr_file;

# run sysrq
system("echo t > /proc/sysrq-trigger");
}
}'
sleep 1m
done


支付宝打赏 微信打赏

赞赏支持一下