深入 awk命令:Linux 命令行中的文本处理神器
一、概述
awk 是一种强大的文本处理工具,最早由 Alfred Aho、Peter Weinberger 和 Brian Kernighan 三位计算机科学家在 1977 年开发,因此取名为 “awk”。他们的目标是提供一种便捷的方式来处理和分析结构化文本数据,特别是针对行和列的格式化数据,如表格和日志文件。
awk 的命名源自三位创始人的姓氏首字母(Aho, Weinberger, Kernighan)。起初,awk 主要用于简单的文本处理和转换。随着使用需求的增加,awk 在后来的版本中添加了更多复杂的功能,如支持正则表达式、内置变量、数组等。
awk 的最初版本使用简单,但功能强大。gawk(GNU AWK)是GNU 项目的一部分,扩展了 awk 的功能,增加了许多新特性,比如用户自定义函数和更强大的模式匹配能力。nawk(New AWK)也是 awk的一个改进版本,主要在 1985 年推出,提供额外功能和兼容性。
awk
的应用:
- 常用于系统管理、数据分析和报告生成等多种领域,其主要优势在于对文本数据的高度灵活性和强大的处理能力。
- 语法简洁明了,非常适合快速编写脚本,进行数据提取和转换。
awk
在文本处理中的重要性:
-
awk 设计初衷是用于文本处理,特别是结构化文本(如 CSV 和日志文件)。其内置的模式匹配和字段处理功能使得处理文本数据的效率非常高。
-
awk 的语法相对简洁,可以用较少的代码实现复杂的文本处理逻辑。这使得编写和维护脚本变得更加容易,特别适合快速的数据处理任务。
-
awk 支持强大的模式匹配功能,包括正则表达式。
-
awk 提供了一系列内置变量(如
$0
,$1
,NF
,NR
等),以及丰富的内置函数,简化用户在数据处理时的复杂性。用户可以轻松实现各种统计和分析功能。 -
awk 可以与其他命令行工具(如 grep 和 sed)结合使用,形成强大的文本处理管道,极大地扩展了 awk 的应用场景。
-
尽管 awk 主要用于文本处理,但它也能执行数值计算、格式化输出等多种功能。
二、awk 基础知识
2.1、什么是 awk ?
awk
同 sed
命令类似,都是一种文本处理工具,只不过 sed
擅长取行, awk
命令擅长取列。awk
广泛用于 Linux 和 Unix 系统的命令行环境中。它以其简洁的语法和强大的功能而著称,特别适合于对结构化文本数据的处理,如表格、日志文件和配置文件等。
定义: awk 是一种编程语言,用于在文件或标准输入中进行模式匹配和操作。它的语法允许用户定义规则和操作,能够处理文本的行与列。
主要作用:
-
文本过滤:awk 可以根据用户定义的模式筛选文本,提取符合条件的行或字段。
-
文本格式化:能够对输入的文本进行格式化输出,比如调整列宽、添加文本等。
-
字段处理:awk 默认将每一行文本分割成多个字段(以空格或指定分隔符为界),用户可以方便地访问和操作这些字段。
-
数值计算:awk 支持多种数值运算,能对数据进行计算,如求和、平均等。
-
报告生成:可以基于处理结果生成总结性报告,方便数据分析和展示。
-
脚本编写:awk 可用于编写复杂的文本处理脚本,易于部署和执行。
awk 命令基本语法格式为:
awk 'pattern { action }' file
- pattern:用于匹配输入行的条件。
- action:对匹配到的行执行的操作。
例子:
-
打印文件的每一行:
awk '{ print }' filename
-
打印指定列(如第二列):
awk '{ print $2 }' filename
-
根据条件筛选行(如打印大于某值的行):
awk '$1 > 10 { print }' filename
-
计算某列的总和:
awk '{ sum += $1 } END { print sum }' filename
原理: 一般是遍历一个文件中的每一行,然后分别对文件的每一行进行处理。
用法:
awk [可选的命令行选项] 'BEGIN{命令 } pattern{ 命令 } END{ 命令 }' 文件名
2.2、awk 的安装与环境
awk 是大多数 Linux 发行版中预装的工具,因此在许多情况下不需要显式安装它。不过,如果你的系统上没有 awk,或者希望安装特定版本,可以按照以下步骤在常见的 Linux 发行版上安装 awk。
在 Debian 或 Ubuntu 及其衍生版上,可以使用 apt
安装 awk(通常是 gawk
,GNU AWK 的实现)。
sudo apt update
sudo apt install gawk
在 RHEL、CentOS 或 Fedora 及其衍生版上,可以使用 yum
或 dnf
来安装 awk
。
# 对于 CentOS/RHEL 7:
sudo yum install gawk
# 对于 Fedora 或 CentOS/RHEL 8:
sudo dnf install gawk
在 Arch Linux 及其衍生版上,可以使用 pacman
安装 awk。
sudo pacman -S gawk
在 OpenSUSE 中,可以使用 zypper
来安装 awk。
sudo zypper install gawk
在 Gentoo 中,可以使用 emerge
来安装 awk。
sudo emerge gawk
安装完成后,可以通过以下命令验证 awk 是否安装成功:
awk --version
三、awk 的基本结构
awk 的基本结构由“模式”(pattern)和“动作”(action)组成。理解这两部分是有效使用 awk 的关键。
模式(pattern)可以是以下几种类型:
- 行号: 指定执行动作的行,例如
1
(第一行)、3
(第三行)。 - 布尔表达式: 可以使用关系运算符(如
==
,!=
,<
,>
,<=
,>=
)进行比较。例如:$2 > 30
(第二列大于 30 的行)。 - 正则表达式: 使用正则表达式匹配字符串,例如
/pattern/
。示例:/Alice/
(包含 “Alice” 的行)。 - 内置变量条件: 如
NR
(当前记录号)和NF
(当前行字段数)。例如,NF > 2
指示只有当当前行字段数大于 2 时才执行动作。
动作(action)通常由一条或多条语句组成,常见类型包括:
- 打印: 使用
print
打印输出。示例:{ print $1, $2 }
(打印第一和第二列)。 - 计算: 可以进行简单的算术运算。例如:
{ sum += $2 }
(将第二列累加到变量sum
)。 - 赋值和条件控制: 使用变量进行赋值,或者使用
if
语句进行条件控制。
示例 1: 打印所有行。
awk '{ print }' test.txt
示例 2: 只打印第二列大于 30 的行。
awk '$2 > 30 { print $1, $2 }' test.txt
示例 3: 使用正则表达式。
awk '/Alice/ { print $0 }' test.txt
默认行为: 如果没有指定模式,awk 默认会处理每一行;如果没有指定动作,awk 默认会执行 print
动作,打印每一匹配的行。
awk “字段”和“记录”的概念是其核心组成部分。在 awk 中,记录通常是指输入文本中的一行。默认情况下,awk 会将每一行视为一条记录。所有记录的集合构成了 awk 的输入。
每条记录可以进一步划分为多个 字段。awk 默认使用空格和制表符作为字段的分隔符。每个字段可以通过特殊变量 $n
访问,其中 n
是字段的位置。例如:
$1
表示第一字段$2
表示第二字段- 依此类推
awk 默认的字段分隔符是空格(
)和制表符(\t
)。这意味着空格和制表符被视为将字段分开的字符。
如果需要使用其他字符作为字段分隔符,可以在 awk 命令中使用 -F
选项来修改。例如:
-
使用逗号
,
作为分隔符:awk -F, '{ print $1, $2 }' file.csv
-
使用冒号
:
作为分隔符:awk -F: '{ print $1, $3 }' /etc/passwd
要处理和访问字段,可以使用以下几种方法:
-
直接打印字段内容:
awk '{ print $1, $2 }' test.txt
-
计算字段的总和。例如,计算第二列的总和:
awk '{ sum += $2 } END { print sum }' test.txt
-
可以根据字段的值进行条件处理,例如只打印第二列值大于 30 的行:
awk '$2 > 30 { print $1, $2 }' test.txt
内置变量:
- NF: 表示当前记录的字段数量,可以用来处理动态字段数量的行。
- NR: 表示当前记录的行号。
四、awk 的常用功能
打印某几列:
$ echo 'I love you' | awk '{print $3 $2 $1}'
youloveI
我们将字符串 I love you
通过管道传递给awk
命令,相当于awk
处理一个文件,该文件的内容就是I love you
,默认通过空格作为分隔符(不管列之间有多少个空格都将当作一个空格处理)I love you
就分割成三列了。假如分割符号为.
,可以这样用:
$ echo '192.168.1.1' | awk -F "." '{print $2}'
168
条件过滤:
我们知道awk
的用法是这样的,那么pattern
部分怎么用呢?
awk [可选的命令行选项] 'BEGIN{命令 } pattern{ 命令 } END{ 命令 }' 文件名
$ cat score.txt
tom 60 60 60
kitty 90 95 87
jack 72 84 99
$ awk '$2>=90{print $0}' score.txt
kitty 90 95 87
$2>=90
表示如果当前行的第 2 列的值大于 90 则处理当前行,否则不处理。说白了 pattern 部分是用来从文件中筛选出需要处理的行进行处理的,这部分是空的代表全部处理。
pattern 部分可以是任何条件表达式的判断结果,例如>,<,==,>=,<=,!=
同时还可以使用+,-,*,/
运算与条件表达式相结合的复合表达式,逻辑 &&,||,!
同样也可以使用进来。另外 pattern 部分还可以使用 /正则/
选择需要处理的行。
判断语句:
判断语句是写在pattern{ 命令 }
命令中的,他具备条件过滤一样的作用,同时他也可以让输出更丰富
$ awk '{if($2>=90 )print $0}' score.txt
kitty 90 95 87
$ awk '{if($2>=90 )print $1,"优秀"; else print $1,"良好"}' score.txt
tom 良好
kitty 优秀
jack 良好
$ awk '{if($2>=90 )print $0,"优秀"; else print $1,"良好"}' score.txt
tom 良好
kitty 90 95 87 优秀
jack 良好
BEGIN 定义表头:
awk [可选的命令行选项] 'BEGIN{命令 } pattern{ 命令 } END{ 命令 }' 文件名
使用方法如下:
$ awk 'BEGIN{print "姓名 语文 数学 英语"}{printf "%-8s%-5d%-5d%-5d\n",$1,$2,$3,$4}' score.txt
姓名 语文数学英语
tom 60 60 60
kitty 90 95 87
jack 72 84 99
这里要注意,我为了输出格式好看,做了左对齐的操作(%-8s左对齐,宽8位),printf
用法和c++类似。
不仅可以用来定义表头,还可以做一些变量初始化的工作,例如
$ awk 'BEGIN{OFMT="%.2f";print 1.2567,12E-2}'
1.26 0.12
这里OFMT
是个内置变量,初始化数字输出格式,保留小数点后两位。
END 添加结尾符: 和BEGIN
用法类似。
$ echo ok | awk '{print $1}END{print "end"}'
ok
end
数据计算:
$ awk 'BEGIN{print "姓名 语文 数学 英语 总成绩"; \
sum1=0;sum2=0;sum3=0;sumall=0} \
{printf "%5s%5d%5d%5d%5d\n",$1,$2,$3,$4,$2+$3+$4;\
sum1+=$2;sum2+=$3;sum3+=$4;sumall+=$2+$3+$4}\
END{printf "%5s%5d%5d%5d%5d\n","总成绩",sum1,sum2,sum3,sumall}'\
score.txt
姓名 语文 数学 英语 总成绩
tom 60 60 60 180
kitty 90 95 87 272
jack 72 84 99 255
总成绩 222 239 246 707
因为命令太长,末尾用\
符号换行。
BEGIN
体里输出表头,并给四个变量初始化0。pattern
体里输出每一行,并累加运算。END
体里输出总统计结果。
当然了,一个正常人在用 Linux 命令的时候是不会输入那么多格式化符号来对齐的,所以新命令又来了column -t
(鬼知道我为什么会记得这么多乱七八糟的命令)。
awk
支持标准的 if
语句,用于根据条件执行不同的操作。例如:打印第二列大于 30 的行,并标记是否超过 50。
awk '{
if ($2 > 50)
print $1, ":", $2, " (High)"
else if ($2 > 30)
print $1, ":", $2, " (Medium)"
else
print $1, ":", $2, " (Low)"
}' test.txt
awk
提供了一些循环结构,包括 for
循环和 while
循环。例如:
awk 'BEGIN {
for (i = 1; i <= 5; i++)
print i
}'
awk 'BEGIN {
i = 1;
while (i <= 5) {
print i;
i++
}
}'
五、awk 范例:网络状态统计
本小节,采用awk
统计netstat
命令的一些网络状态,来看一下awk
语言的基本要素。netstat
的输出类似于:
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 localhost:42373 localhost:52530 ESTABLISHED
tcp 0 0 localhost:42373 localhost:44906 ESTABLISHED
tcp 0 0 localhost:52530 localhost:42373 ESTABLISHED
tcp 0 0 localhost:44906 localhost:42373 ESTABLISHED
tcp 0 0 172.28.129.124:51366 ubuntu-mirror-1.ps:http TIME_WAIT
Active UNIX domain sockets (w/o servers)
Proto RefCnt Flags Type State I-Node Path
unix 2 [ ] DGRAM 18547 /var/run/chrony/chronyd.sock
unix 3 [ ] DGRAM CONNECTED 21586 /run/systemd/notify
unix 2 [ ] DGRAM 20867 /run/user/1000/systemd/notify
unix 2 [ ] DGRAM 21595 /run/systemd/journal/syslog
unix 9 [ ] DGRAM CONNECTED 21603 /run/systemd/journal/dev-log
unix 7 [ ] DGRAM CONNECTED 21605 /run/systemd/journal/socket
unix 3 [ ] STREAM CONNECTED 1368
unix 2 [ ] DGRAM 62094
unix 3 [ ] STREAM CONNECTED 740099
......
其中,第 6 列,标明了网络连接所处于的网络状态。我们先给出awk
命令,看一下统计结果。
netstat -ant |
awk ' \
BEGIN{print "State","Count" } \
/^tcp/ \
{ rt[$6]++ } \
END{ for(i in rt){print i,rt[i]} }'
netstat -ant |
awk ' \
BEGIN{print "State","Count" } \
/^tcp/ \
{ if($4=="0.0.0.0:3306" ) rt[$6]++ } \
END{ for(i in rt){print i,rt[i]} }'
输出结果为:
State Count
LAST_ACK 1
LISTEN 64
CLOSE_WAIT 43
ESTABLISHED 719
SYN_SENT 5
TIME_WAIT 146
下面这张图会配合以上命令详细说明,希望你能了解awk
的精髓。
乍一看,好吓人的命令,但是很简单。awk
和我们通常的程序不太一样,它分为四个部分。
- BEGIN 开头部分,可选的。用来设置一些参数,输出一些表头,定义一些变量等。上面的命令仅打印了一行信息而已。
- END 结尾部分,可选的。用来计算一些汇总逻辑,或者输出这些内容。上面的命令,使用简单的for循环,输出了数组rt中的内容。
- Pattern 匹配部分,依然可选。用来匹配一些需要处理的行。上面的命令,只匹配tcp开头的行,其他的不进入处理。
- Action 模块。主要逻辑体,按行处理,统计打印,都可以。
注意点:
awk
的主程序部分使用单引号‘
包围,而不能是双引号。awk
的列开始的index
是 0,而不是1。
六、总结
awk 作为一种高效的文本处理工具,其功能强大且灵活,适用于多种数据处理任务。从基础语法到复杂的条件和循环结构,awk 提供了丰富的操作选项。文章通过实例详细介绍了它在文本筛选、格式化输出和数据计算等方面的应用,展示了 awk 在系统管理和数据分析中的重要性。通过掌握 awk,用户不仅能够提升文本处理的效率,还能够轻松编写和维护复杂的脚本,为工作中带来更大的便利。