linux-shell-04 shell-脚本四剑客-awk

linux-shell-04 shell-脚本四剑客-awk

概念解析

  AWK是一种处理文本文件的语言,是一个强大的文本分析工具。之所以叫AWK是因为其取了三位创始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符。

  awk本身就是一种编程语言,它支持条件判断、循环、数组遍历等功能。我们可以使用它来对文本进行分析和处理。与sed相比,sed是逐行处理文本内数据,awk也是,不过awk又在此基础上实现对每一行每一列的操作。专业的角度来讲awk是一个报告生成器,因为awk可以对行和列进行操作,所以在awk命令下,一个文本就像是一个表格。默认列之间的分隔符是空格,行之间分隔符是\n,可以通过-F或者-v FS:指定分割符。

语法

1
2
3
awk [选项参数] 'script' var=value file(s)

awk [选项参数] -f scriptfile var=value file(s)

选项参数说明

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
27
28
29
30
31
32
33
34
35
36
37
38
-F fs or --field-separator fs
指定输入文件折分隔符,fs是一个字符串或者是一个正则表达式,如-F:。

-v var=value or --asign var=value
赋值一个用户定义变量。

-f scripfile or --file scriptfile
从脚本文件中读取awk命令。

-mf nnn and -mr nnn
对nnn值设置内在限制,-mf选项限制分配给nnn的最大块数目;-mr选项限制记录的最大数目。这两个功能是Bell实验室版awk的扩展功能,在标准awk中不适用。

-W compact or --compat, -W traditional or --traditional
在兼容模式下运行awk。所以gawk的行为和标准的awk完全一样,所有的awk扩展都被忽略。

-W copyleft or --copyleft, -W copyright or --copyright
打印简短的版权信息。

-W help or --help, -W usage or --usage
打印全部awk选项和每个选项的简短说明。

-W lint or --lint
打印不能向传统unix平台移植的结构的警告。

-W lint-old or --lint-old
打印关于不能向传统unix平台移植的结构的警告。

-W posix
打开兼容模式。但有以下限制,不识别:/x、函数关键字、func、换码序列以及当fs是一个空格时,将新行作为一个域分隔符;操作符**和**=不能代替^和^=;fflush无效。

-W re-interval or --re-inerval
允许间隔正则表达式的使用,参考(grep中的Posix字符类),如括号表达式[[:alpha:]]。

-W source program-text or --source program-text
使用program-text作为源代码,可与-f命令混用。

-W version or --version
打印bug报告信息的版本。

基本用法

用法一

1
awk '{pattern + action}' {filenames}

1.基本语法参数
1.单引号‘’:为了和shell语法分开,防止混淆,行匹配语句 awk ‘’ 只能用单引号
2.大括号{}:用来表示一组命令
3.pattern:一个过滤器,表示匹配pattern的条件的行才可以执行action
4.pattern可以是以下任意一种
-正则表达式:使用通配符匹配
-关系表达式:使用关系运算符
-模式匹配表达式:使用运算符~或者!~匹配
5.action:处理动作,一系列命令,常见的动作为print

实例:每行按空格或TAB分割,输出文本中的1、4项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@localhost ~]# vim 1.txt
[root@localhost ~]# cat 1.txt
1 2 3 4 5
6 7 8 9 10
a b c d e
hello world and awk
languege php,java,python

[root@localhost ~]# awk '{print $1,$4}' 1.txt
1 4
6 9
a d
hello awk
languege

[root@localhost ~]# awk '{print $1,$2}' 1.txt
1 2
6 7
a b
hello world
languege php,java,python

[root@localhost ~]#

用法二

1
awk -F  	#-F相当于内置变量FS, 指定分割字符

实例: 使用”,”作为分割字符

1
2
3
4
5
6
7
8
[root@localhost ~]# awk -F, '{print $1,$2}' 1.txt 
1 2 3 4 5
6 7 8 9 10
a b c d e
hello world and awk
languege php java

[root@localhost ~]#

用法三

1
awk -v  # 设置变量

实例:设置一个叫做xxx的变量,值为100,那么$1+xxx=每行第一个项加100的和,$1xxx表示每行第一个项与xxx衔接的结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@localhost ~]# cat 1.txt 
1 2 3 4 5
6 7 8 9 10
a b c d e
hello world and awk
languege php,java,python


[root@localhost ~]# awk -v xxx=100 '{print $1,$1+xxx,$1xxx}' 1.txt
1 101 1100
6 106 6100
a 100 a100
hello 100 hello100
languege 100 languege100
100 100
[root@localhost ~]#

用法四

1
awk -f {awk脚本} {文件名}

实例:

1
2
3
4
5
6
7
8
9
10
[root@localhost ~]# vim test.awk 
[root@localhost ~]# cat test.awk
#! /usr/bin/awk -f
BEGIN { printf "%s","Writing my first awk executable script!\n" }
[root@localhost ~]# chmod +x test.awk
[root@localhost ~]# ./test.awk
Writing my first awk executable script!
[root@localhost ~]# awk -f test.awk
Writing my first awk executable script!
[root@localhost ~]#

awk脚本首行都是#! /usr/bin/awk -f,以为awk解释器的路径就是 /usr/bin/awk。

运算符

运算符 描述
= += -= *= /= %= ^= **= 赋值
?: C条件表达式
\ \ 逻辑或
&& 逻辑与
~ ~! 匹配正则表达式和不匹配正则表达式
< <= > >= != == 关系运算符
空格 连接
+ - 加,减
* / % 乘,除与求余
+ - ! 一元加,减和逻辑非
^ *** 求幂
++ – 增加或减少,作为前缀或后缀
$ 字段引用
in 数组成员

运算符实例

过滤出第一列大于2的行

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@localhost ~]# cat 1.txt 
1 2 3 4 5
6 7 8 9 10
a b c d e
hello world and awk
languege php,java,python

[root@localhost ~]# awk '$1>2' 1.txt
6 7 8 9 10
a b c d e
hello world and awk
languege php,java,python
[root@localhost ~]#

过滤第一列等于6的行

1
2
3
[root@localhost ~]# awk '$1==6' 1.txt 
6 7 8 9 10
[root@localhost ~]#

过滤第一列大于2并且第二列等于7的行

1
2
3
[root@localhost ~]# awk '$1>2&& $2==7' 1.txt 
6 7 8 9 10
[root@localhost ~]#

使用正则,字符串匹配

输出第二列包含 “python”的那一行的第1列与第2列。

1
2
3
[root@localhost ~]# awk '$2 ~ /python/ {print $1,$2}' 1.txt 
languege php,java,python
[root@localhost ~]#

注意:~ 表示模式开始。// 中是模式。

输出包含”java” 的行

1
2
3
[root@localhost ~]# awk '/java/' 1.txt 
languege php,java,python
[root@localhost ~]#

给1.txt增加一行大写内容

1
2
3
4
5
6
7
8
9
[root@promote ~]# vim 1.txt
[root@promote ~]# cat 1.txt
1 2 3 4 5
6 7 8 9 10
a b c d e
hello world and awk
languege php,java,python
LANGUEGE PHP,JAVA,PYTHON
[root@promote ~]#

输出包含java的行,不区分大小写

1
2
3
4
5
6
7
[root@promote ~]# awk 'BEGIN{IGNORECASE=1} /java/' 1.txt 
languege php,java,python
LANGUEGE PHP,JAVA,PYTHON
[root@promote ~]# awk 'BEGIN{IGNORECASE=1} /JavA/' 1.txt
languege php,java,python
LANGUEGE PHP,JAVA,PYTHON
[root@promote ~]#

输出不包含java的行,不区分大小写

1
2
3
4
5
6
[root@promote ~]# awk 'BEGIN{IGNORECASE=1} !/JavA/' 1.txt 
1 2 3 4 5
6 7 8 9 10
a b c d e
hello world and awk
[root@promote ~]#

awk脚本

helloworld

1
2
3
4
5
6
7
8
[root@promote ~]# vim a.awk
[root@promote ~]# cat a.awk
#!/bin/awk -f
BEGIN { print "Hello, world!" }
[root@promote ~]# chmod +x a.awk
[root@promote ~]# ./a.awk
Hello, world!
[root@promote ~]#

关于awk脚本,我们需要注意两个关键词BEGIN和END。

  • BEGIN{ 这里面放的是执行前的语句 }
  • END {这里面放的是处理完所有的行后要执行的语句 }
  • 中间{这里面放的是处理每一行时要执行的语句}
1
2
3
4
5
[root@node4 ~]# cat a.awk 
#!/bin/awk -f
BEGIN { print "before execute" }
{ print "now is executing" }
END { print "after execute"}

执行结果如下:

敲回车后进入到中间部分的代码

可是现在无论如何敲回车,也没有办法退出该部分代码,进入不到end部分。原因是执行脚本的时候没有参数。

先建一个2.txt作为测试对象

1
2
3
4
[root@node4 ~]# cat 2.txt 
1 2 3 4 5
6 7 8 9 10
[root@node4 ~]#

修改脚本,然后执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@node4 ~]# vim a.awk 
[root@node4 ~]# cat a.awk
#!/bin/awk -f
BEGIN { printf "before execute\n" }
{
printf "now is executing, print_file_content:\n"
printf $0
printf "\n"
}
END { printf "after execute\n"}
[root@node4 ~]# awk -f a.awk 2.txt
before execute
now is executing, print_file_content:
1 2 3 4 5
now is executing, print_file_content:
6 7 8 9 10
after execute
[root@node4 ~]#

awk用于分割字符串

awk在写脚本的时候,用得比较多的是awk -F来切割字符串的场景。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#默认情况下awk以空格作为分隔符
[root@localhost ~]# echo "hello world" | awk '{print $1;print $2}'
hello
world
[root@localhost ~]#


#awk -F可以指定分隔符
[root@localhost ~]# echo "age:23 color:red" | awk -F ':' '{print $1;print $2;print $3}'
age
23 color
red
[root@localhost ~]#

#如何同时取出"age:23 color:red"里面的 “23”和“red”值呢?
[root@localhost ~]# echo "age:23 color:red" | awk '{print $1;print $2}'
age:23
color:red
[root@localhost ~]# echo "age:23 color:red" | awk '{print $1;print $2}' | awk -F ':' '{print $2}'
23
red
[root@localhost ~]#

awk支持多个记录分隔符的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#如何同时取出"age:23 color:red"里面的 “23”和“red”值呢?
[root@localhost ~]# echo "age:23 color:red" | awk -F '[ :]' '{print $2;print $4}'
23
red
[root@localhost ~]#

注意,上面这条命令中,'[ :]'表示分隔符是空格和冒号。


#上面的分割方式一般不常用,因为文档里有可能出现两个分割服恰好在一起的情况
[root@localhost ~]# echo "age:23 : : color:red" | awk -F '[ :]' '{print $2;print $4}'
23

[root@localhost ~]#
此时输出的$4就是一个空格了,显然“ : : ”在这里是算5次分割,而不是1次。



#'[ :]+'比之前多了一个加号,表明将连续出现的记录分隔符当做一个来处理,这个比较常用
[root@localhost ~]# echo "age:23 : : color:red" | awk -F '[ :]+' '{print $2;print $4}'
23
red
[root@localhost ~]#

多个分隔符之间用管道符隔开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#注意分割后的段数为NF
[root@localhost ~]# echo "abc 1 def 1 ghi 1 jkl" | awk -F '1' '{print "切割后段数为:" NF}'
切割后段数为:4
[root@localhost ~]#

#如何以one和two为分割符,换行输出句子里的其他部分。

[root@localhost ~]# echo "i have one pen and i have two cups" | awk -F 'one|two' '{for(i=1;i<=NF;i++)print $i}'
i have
pen and i have
cups
[root@localhost ~]#


#如何以one和two和three为分割符,换行输出句子里的其他部分。

[root@localhost ~]# echo "i have one pen and i have two cups and i have three apples" | awk -F 'one|two|three' '{for(i=1;i<=NF;i++)print $i}'
i have
pen and i have
cups and i have
apples
[root@localhost ~]#

本篇到此结束

欢迎打赏,谢谢
------ 本文结束------
0%