Linux文本处理三剑客之awk

本文针对linux中的文本处理“三剑客”之一的awk命令进行使用解释,awk对以行为单位对文本数据进行处理

1
2
#查看系统环境
!lsb_release -a

输出:

1
2
3
4
5
No LSB modules are available.
Distributor ID: LinuxMint
Description: Linux Mint 19.3 Tricia
Release: 19.3
Codename: tricia

1
2
#查看文本内容
!cat awk_text

输出:

1
2
3
4
5
1 M.Tansley     05/99     48311     Green     8     40     44
2 J.Lulu 06/99 48317 green 9 24 26
3 P.Bunny,02/99,48,Yellow,12,35,28
4 J.Troll:07/99:4842:Brown-3:12:26:26
5 L.Tansley:05/99:4712:Brown-2:12:30:28

注意:jupyter运行linux指令需要在指令前加!

命令格式

awk 命令的基本格式为:

1
awk 'BEGIN{ 命令1 } 模式{ 命令2 } END{ 命令3 }' 文件

awk工作流程

  • 1、BEGIN 执行 BEGIN 块的内容,即命令1内容,可选。
  • 2、完成 BEGIN 块的执行,开始执行body块。
  • 3、读入有 \n 换行符分割的记录。
  • 4、将记录按指定的域分隔符划分域,填充域。
  • 5、依次执行各 BODY 块,pattern 部分匹配该行内容成功后,才会执行命令2的内容。
  • 6、循环读取并执行各行直到文件结束,完成body块执行。
  • 7、开始 END 块执行,命令3内容,END 块可以输出最终结果,可选。

选用参数

选项含义
-F fs指定以 fs 作为输入行的分隔符,awk 命令默认分隔符为空格或制表符。
-f file从脚本文件中读取 awk 脚本指令,以取代直接在命令行中输入指令。
-v var=val在执行处理过程之前,设置一个变量 var,并给其设备初始值为 val。

awk 命令的脚本命令:

1
'匹配规则{执行命令}'

1.匹配规则:可以使用字符串(比如 /demo/,表示查看含有 demo 字符串的行)或者正则表达式指定

2.执行命令:需要用大括号({})括起来

注意:

  • 整个脚本命令是用单引号(‘’)括起
  • 在 awk 程序执行时,如果没有指定执行命令,则默认会把匹配的行输出;如果不指定匹配规则,则默认匹配文本中所有的行。

awk 命令的运行方式:

    1. awk 命令行: # awk
    1. awk 程序文件: # awk -f /data/awk_script//将 swk 命令写到文件中,然后调用这个文件
  • 3.awk脚本: #!/bin/awk -f

详细使用

指定分隔符

1
2
3
!awk -F: '{print $1}' awk_text
print('--------------------------------------------------------------')
!awk -F: '{print $1}' awk_text

输出:

1
2
3
4
5
6
7
8
9
10
11
1 M.Tansley     05/99     48311     Green     8     40     44
2 J.Lulu 06/99 48317 green 9 24 26
3 P.Bunny,02/99,48,Yellow,12,35,28
4 J.Troll
5 L.Tansley
--------------------------------------------------------------
1 M.Tansley 05/99 48311 Green 8 40 44
2 J.Lulu 06/99 48317 green 9 24 26
3 P.Bunny,02/99,48,Yellow,12,35,28
4 J.Troll
5 L.Tansley

从文件中读取程序

跟 sed 一样,awk 允许将脚本命令存储到文件中,然后再在命令行中引用

1
!cat awk.sh

输出:

1
{print  "The fifth number after"  $1  "is"   $6}

1
!awk -f awk.sh awk_text

输出:

1
2
3
4
5
The fifth number after1is8
The fifth number after2is9
The fifth number after3is
The fifth number after4is
The fifth number after5is

1
!cat awk.func

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/awk -f
#运行前
BEGIN {
math = 0
english = 0
computer = 0
printf "NAME NO. MATH ENGLISH COMPUTER TOTAL\n"
printf "---------------------------------------------\n"
}
#运行中
{
math+=$3
english+=$4
computer+=$5
printf "%-6s %-6s %4d %8d %8d %8d\n", $1, $2, $3,$4,$5, $3+$4+$5
}
#运行后
END {
printf "---------------------------------------------\n"
printf " TOTAL:%10d %8d %8d \n", math, english, computer
printf "AVERAGE:%10.2f %8.2f %8.2f\n", math/NR, english/NR, computer/NR
}

1
!awk -f awk.func score

输出:

1
2
3
4
5
6
7
8
9
10
NAME    NO.   MATH  ENGLISH  COMPUTER   TOTAL
---------------------------------------------
Marry 2143 78 84 77 239
Jack 2321 66 78 45 189
Tom 2122 48 77 71 196
Mike 2537 87 97 95 279
Bob 2415 40 57 62 159
---------------------------------------------
TOTAL: 319 393 350
AVERAGE: 63.80 78.60 70.00

设置变量

1
!awk -v a=1 '{print NR,$2,$2+a}' awk_text

输出:

1
2
3
4
5
1 M.Tansley 1
2 J.Lulu 1
3 P.Bunny,02/99,48,Yellow,12,35,28 1
4 J.Troll:07/99:4842:Brown-3:12:26:26 1
5 L.Tansley:05/99:4712:Brown-2:12:30:28 1

内建变量

序号变量描述
1$n当前记录的第n个字段,字段间由FS分隔
2$0完整的输入记录
3ARGC命令行参数的数目
4ARGIND命令行中当前文件的位置(从0开始算)
5ARGV包含命令行参数的数组
6CONVFMT数字转换格式(默认值为%.6g)ENVIRON环境变量关联数组
7ERRNO最后一个系统错误的描述
8FIELDWIDTHS字段宽度列表(用空格键分隔)
9FILENAME当前文件名
10FNR各文件分别计数的行号
11FS字段分隔符(默认是任何空格)
12IGNORECASE如果为真,则进行忽略大小写的匹配
13NF一条记录的字段的数目
14NR已经读出的记录数,就是行号,从1开始
15OFMT数字的输出格式(默认值是%.6g)
16OFS输出记录分隔符(输出换行符),输出时用指定的符号代替换行符
17ORS输出记录分隔符(默认值是一个换行符)
18RLENGTH由match函数所匹配的字符串的长度
19RS记录分隔符(默认是一个换行符)
20RSTART由match函数所匹配的字符串的第一个位置
21SUBSEP数组下标分隔符(默认值是/034)

数据字段变量

awk 会自动给一行中的每个数据元素分配一个变量

变量含义
$0代表整个文本行;
$1代表文本行中的第 1 个数据字段;
$2代表文本行中的第 2 个数据字段;
$n代表文本行中的第 n 个数据字段。

注意:在 awk 中,默认的字段分隔符是任意的空白字符(例如空格或制表符),awk 在读取一行文本时,会用预定义的字段分隔符划分每个数据字段

1
2
3
4
5
6
7
8
# 查看全部
!awk '{print $0}' awk_text
print('--------------------------------------------------------')
# 查看每一行的第一个内容
!awk '{print $1}' awk_text
print('--------------------------------------------------------')
# 查看每一行的最后一个内容
!awk '{print $6}' awk_text

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1 M.Tansley     05/99     48311     Green     8     40     44
2 J.Lulu 06/99 48317 green 9 24 26
3 P.Bunny,02/99,48,Yellow,12,35,28
4 J.Troll:07/99:4842:Brown-3:12:26:26
5 L.Tansley:05/99:4712:Brown-2:12:30:28
--------------------------------------------------------
1
2
3
4
5
--------------------------------------------------------
8
9

其他参数

1
!awk 'BEGIN{printf "%4s %4s %4s %4s %4s %4s %4s %4s %4s\n","FILENAME","ARGC","FNR","FS","NF","NR","OFS","ORS","RS";printf "---------------------------------------------\n"} {printf "%4s %4s %4s %4s %4s %4s %4s %4s %4s\n",FILENAME,ARGC,FNR,FS,NF,NR,OFS,ORS,RS}' awk_text

输出:

1
2
3
4
5
6
7
FILENAME ARGC  FNR   FS   NF   NR  OFS  ORS   RS
---------------------------------------------
awk_text 2 1 8 1
awk_text 2 2 8 2
awk_text 2 3 2 3
awk_text 2 4 2 4
awk_text 2 5 2 5

1
!awk 'BEGIN{FS="[ :]+"}{print  $1,$2}'   awk_text

输出:

1
2
3
4
5
1 M.Tansley
2 J.Lulu
3 P.Bunny,02/99,48,Yellow,12,35,28
4 J.Troll
5 L.Tansley

多个命令

awk 允许将多条命令组合成一个正常的程序。要在命令行上的程序脚本中使用多条命令,只要在命令之间放个分号即可

1
!echo "My name is Tom" | awk '{$4="ann";print $0}'

输出:

1
My name is ann

BEGIN关键字与END关键字

1
!awk 'BEGIN { for (i = 1; i <= 5; ++i) print i }'

输出:

1
2
3
4
5
1
2
3
4
5

printf指定输出格式

printf “FORMAT” ,item1,item2,…//指格式化输出,必须指定FORMAT,不会自动换行,换行使用换行符\n;FORMAT中需要为每个item指定格式符:与item–对应

%c: 显示字符的ASCII码
%d, %i: 显示十进制整数
%e, %E:显示科学计数法数值
%f:显示为浮点数
%g, %G:以科学计数法或浮点形式显示数值
%s:显示字符串
%u:无符号整数
%%: 显示%自身

修饰符:
#[.#]:第一个数字控制显示的宽度;第二个#表示小数点后精度,%3.1f
-: 左对齐(默认右对齐) %-15s
+:显示数值的正负符号 %+d

1
2
!awk 'BEGIN {printf "%s %s %-4.2f\n","TOM","man",66.1234}'
!awk 'BEGIN {printf "%-10s %-8s %-4.2f\n","TOM","man",66.1234}'

输出:

1
2
TOM man 66.12
TOM man 66.12

操作符

  • 算数操作符 : x+y, x-y,x*y, x/y, x^y, x%y;-x: 转换为负数 ;+x: 转换为数值
  • 赋值操作符 : =, +=, -=, *=, /=, %=, ^= ++, –
  • 比较操作符:==, !=, >, >=, <, <=
  • 模式匹配符:~:左边是否和右边匹配包含 !~:是否不匹配
  • 逻辑操作符:与&&,或||,非!
  • 条件表达式(三目表达式):
1
2
!echo "1.7 2.52" | awk '{printf ("%2f\n",$1)}'
!echo "1.7 2.52" | awk '{printf ("%d\n",$2)}'

输出:

1
2
1.700000
2

分支语句

1
2
!awk 'BEGIN {num = 10; if (num % 2 == 0) printf "%d 是偶数\n", num }'
!awk 'BEGIN {num = 11; if (num % 2 == 0) printf "%d 是偶数\n", num; else printf "%d 是奇数\n", num }'

输出:

1
2
10 是偶数
11 是奇数

循环语句

1
2
3
4
5
6
7
8
9
10
print('For----------------------------------------------')
!awk 'BEGIN { for (i = 1; i <= 2; ++i) print i }'
print('While----------------------------------------------')
!awk 'BEGIN {i = 1; while (i < 3) { print i; ++i } }'
print('Break----------------------------------------------')
!awk 'BEGIN {sum = 0; for (i = 0; i < 10; ++i) {sum += i; if (sum > 15) break; else print "Sum =", sum }}'
print('Continue----------------------------------------------')
!awk 'BEGIN {for (i = 1; i <= 10; ++i) {if (i % 2 == 0) print i; else continue;}}'
print('Exit----------------------------------------------')
!awk 'BEGIN {sum = 0; for (i = 0; i < 10; ++i) {sum += i; if (sum > 15) exit(10); else print "Sum =", sum }}'

输出:

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
26
For----------------------------------------------
1
2
While----------------------------------------------
1
2
Break----------------------------------------------
Sum = 0
Sum = 1
Sum = 3
Sum = 6
Sum = 10
Sum = 15
Continue----------------------------------------------
2
4
6
8
10
Exit----------------------------------------------
Sum = 0
Sum = 1
Sum = 3
Sum = 6
Sum = 10
Sum = 15

数组

语法格式:

1
array_name[index]=value

1
!awk 'BEGIN {sites["runoob"]="www.runoob.com";sites["google"]="www.google.com";print sites["runoob"] "\n" sites["google"]}'

输出:

1
2
www.runoob.com
www.google.com

函数

自定义函数

语法格式:

1
2
3
4
function function_name(argument1, argument2, ...)
{
function body
}

注意:

  • 1.function_name 是用户自定义函数的名称。函数名称应该以字母开头,其后可以是数字、字母或下划线的自由组合。AWK 保留的关键字不能作为用户自定义函数的名称。
  • 2.自定义函数可以接受多个输入参数,这些参数之间通过逗号分隔。参数并不是必须的。我们也可以定义没有任何输入参数的函数。
  • 3.function body 是函数体部分,它包含 AWK 程序代码。
1
!cat awk.func2
# 返回最小值
function find_min(num1, num2)
{
  if (num1 < num2)
    return num1
  return num2
}

# 返回最大值
function find_max(num1, num2)
{
  if (num1 > num2)
    return num1
  return num2
}

# 主函数
function main(num1, num2)
{
  # 查找最小值
  result = find_min(10, 20)
  print "Minimum =", result

  # 查找最大值
  result = find_max(10, 20)
  print "Maximum =", result
}

# 脚本从这里开始执行
BEGIN {
  main(10, 20)
}  

1
!awk -f awk.func2

输出:

1
2
Minimum = 10
Maximum = 20

内置函数

1
2
3
4
5
!awk 'BEGIN {param = 5; result = exp(param); printf "The exponential value of %f is %f.\n", param, result;}'
print('------------------------------------------')
!awk 'BEGIN {str = "Hello, World";print "String before replacement = " str;sub("World", "Jerry", str);print "String after replacement = " str}'
print('------------------------------------------')
!awk 'BEGIN{info="this is a test2012test!";print index(info,"11111")?"ok":"no found";}'

输出:

1
2
3
4
5
6
The exponential value of 5.000000 is 148.413159.
------------------------------------------
String before replacement = Hello, World
String after replacement = Hello, Jerry
------------------------------------------
no found

实例列举

1
2
#从文件中找出长度大于10的行
!awk 'length>38' awk_text

输出:

1
2
3
1 M.Tansley     05/99     48311     Green     8     40     44
2 J.Lulu 06/99 48317 green 9 24 26
5 L.Tansley:05/99:4712:Brown-2:12:30:28

1
2
#打印Hello world
!awk 'BEGIN { print "Hello, world!" }'

输出:

1
Hello, world!

1
2
#9*9乘法表
!seq 9 | sed 'H;g' | awk -v RS='' '{for(i=1;i<=NF;i++)printf("%dx%d=%d%s", i, NR, i*NR, i==NR?"\n":"\t")}'

输出:

1
2
3
4
5
6
7
8
9
1x1=1
1x2=2 2x2=4
1x3=3 2x3=6 3x3=9
1x4=4 2x4=8 3x4=12 4x4=16
1x5=5 2x5=10 3x5=15 4x5=20 5x5=25
1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36
1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49
1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64
1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81