0%

awk的使用

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

grep 、sed、awk 被称为 linux 中的”三剑客”。grep 更适合单纯的查找或匹配文本, sed 更适合编辑匹配到的文本, awk 更适合格式化文本,对文本进行较复杂格式处理。

1. 使用

1.1 分割字符

1
2
3
echo 'this is a test' | awk '{print $0}'

this is a test

print 是打印命令,$0 代表当前行全部字段。上面代码中,print $0 就是把标准输入 this is a test,重新打印了一遍。

awk 会根据空格和制表符,将每一行分成若干字段,依次用 $1、$2、$3 代表第一个字段、第二个字段、第三个字段等等。

1
2
3
echo 'this is a test' | awk '{print $3}'

a

下面,为了便于举例,我们把/etc/passwd 文件保存成 demo.txt。

1
2
3
4
5
root:x:0:0:root:/root:/usr/bin/zsh
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync

这个文件的字段分隔符是冒号(:),所以要用 -F 参数指定分隔符为冒号。然后,才能提取到它的第一个字段。

1
2
3
4
5
6
7
8
9
10
awk -F ':' '{ print $1 }' demo.txt
awk -v FS=':' '{print $1}' demo.txt
cat demo.txt | awk -F ':' '{print $1}'


root
daemon
bin
sys
sync

1.2 变量使用

1.2.1 NF 字段

变量 NF 表示当前行有多少个字段,因此 $NF 就代表最后一个字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 输出倒数第一个字段
echo 'this is a test' | awk '{print $NF}'
test

# 输出倒数第二个字段
echo 'this is a test' | awk '{print $(NF-1)}'
a

# print 命令里面的逗号,表示输出的时候,两个部分之间使用空格分隔。
awk -F ':' '{print $1, $(NF-1)}' demo.txt
root /root
daemon /usr/sbin
bin /bin
sys /dev
sync /bin
1.2.2 NR 行

变量 NR 表示当前处理的是第几行。

1
2
3
4
5
6
7
8
9
# print 命令里面,如果原样输出字符,要放在双引号里面。
awk -F ':' '{print NR ") " $1}' demo.txt


1) root
2) daemon
3) bin
4) sys
5) sync
1.2.3 常用内置变量
1
2
3
4
5
6
FILENAME:当前文件名
FS:字段分隔符,默认是空格和制表符。
RS:行分隔符,用于分割每一行,默认是换行符。
OFS:输出字段的分隔符,用于打印时分隔字段,默认为空格。
ORS:输出记录的分隔符,用于打印时分隔记录,默认为换行符。
OFMT:数字输出的格式,默认为%.6g。

1.3 函数使用

1.3.1 Toupper
1
2
3
4
5
6
7
8
# 函数 `toupper()` 用于将字符转为大写。
awk -F ':' '{ print toupper($1) }' demo.txt

ROOT
DAEMON
BIN
SYS
SYNC
1.3.2 常用内置函数
1
2
3
4
5
6
7
8
//常用函数:
tolower():字符转为小写。
length():返回字符串长度。
substr():返回子字符串。
sin():正弦。
cos():余弦。
sqrt():平方根。
rand():随机数。

1.4 条件使用

awk 允许指定输出条件,只输出符合条件的行。输出条件要写在动作的前面。

1
awk '条件 动作' 文件名
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
# print 命令前面是一个正则表达式,只输出包含 usr 的行。
awk -F ':' '/usr/ {print $1}' demo.txt

root
daemon
bin
sys


# 输出奇数行
awk -F ':' 'NR % 2 == 1 {print $1}' demo.txt

root
bin
sync

# 输出第三行以后的行
awk -F ':' 'NR >3 {print $1}' demo.txt

sys
sync

# 输出第一个字段等于指定值的行。
awk -F ':' '$1 == "root" || $1 == "bin" {print $0}' demo.txt

root:x:0:0:root:/root:/bin/bash
bin:x:2:2:bin:/bin:/usr/sbin/nologin

1.5 If 语句

awk 提供了 if 结构,用于编写复杂的条件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 输出第一个字段的第一个字符大于`m`的行。
awk -F ':' '{if ($1 > "m") print $1}' demo.txt

root
sys
sync


# `if`结构还可以指定`else`部分。
awk -F ':' '{if ($1 > "m") print $1; else print "---"}' demo.txt

root
---
---
sys
sync

1.6 循环使用

END 的意思是“处理完所有的行的标识”,除了 END 还有 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
26
27
28
29
30
31
32
33
34
35
36
37
# 计算多个文件的大小
ls -l *.log
-rw-rw-r-- 1 web web 42487 Nov 12 2021 openfiles1111.log
-rw-rw-r-- 1 web web 2690 Nov 12 2021 openfiles1112.log


ls -l *.log | awk '{sum+=$5} END {print sum}'
45177



# 循环计算内存
ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.3 0.0 171948 11688 ? Ss 2022 1087:09 /lib/systemd/systemd --system --deserialize 29
root 2 0.0 0.0 0 0 ? S 2022 0:36 [kthreadd]
root 3 0.0 0.0 0 0 ? I< 2022 0:00 [rcu_gp]
root 4 0.0 0.0 0 0 ? I< 2022 0:00 [rcu_par_gp]


ps aux | awk 'NR!=1{a[$1]+=$6;} END { for(i in a) print i ", " a[i]"KB";}'

systemd+, 179468KB
ubuntu, 176548KB
web, 1073020KB
message+, 6484KB
syslog, 4728KB
mongodb, 407824KB
redis, 19880KB
consul, 47884KB
grafana, 71188KB
nobody, 4116KB
www-data, 69308KB
uuidd, 1032KB
mysql, 1420364KB
daemon, 2180KB
root, 4716836KB

2. Awk 脚本

2.1 执行逻辑

1
awk 'BEGIN{ commands } pattern{ commands } END{ commands }'

BEGIN 语句块在 awk 开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在 BEGIN 语句块中。

END 语句块 在 awk 从输入流中读取完所有的行 之后 即被执行,比如打印所有行的分析结果这类信息汇总都是在 END 语句块中完成,它也是一个可选语句块。

pattern 语句块中的通用命令是最重要的部分,它也是可选的。如果没有提供 pattern 语句块,则默认执行{ print },即打印每一个读取到的行,awk 读取的每一行都会执行该语句块。

1
2
3
4
5
6
7
echo -e "A line 1\nA line 2" | awk 'BEGIN{ print "Start" } { print } END { print "End" }'


Start
A line 1
A line 2
End

2.2 将外部变量值传递给 awk

借助 -v 选项,可以将外部值(并非来自 stdin)传递给 awk:

1
2
3
4
VAR=10000
echo | awk -v VARIABLE=$VAR '{ print VARIABLE }'

10000

另一种传递外部变量方法:变量之间用空格分隔作为 awk 的命令行参数跟随在 BEGIN、{}和 END 语句块之后。

1
2
3
4
5
var1="aaa"
var2="bbb"
echo | awk '{ print v1,v2 }' v1=$var1 v2=$var2

aaa bbb

2.3 测试

1
2
3
4
5
6
# 查看3306的 pid
netstat -anp | grep 3306 | awk '$NF != "-" {print $NF}' | awk -F '/' '{print $1}'


# 从文件中找出长度大于80的行:
awk 'length>80' demo.txt

3. 参考资料

给作者打赏,可以加首页微信,咨询作者相关问题!