前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux 下 Awk 命令及示例

Linux 下 Awk 命令及示例

作者头像
雪梦科技
发布2020-05-09 17:13:04
1.4K0
发布2020-05-09 17:13:04
举报
文章被收录于专栏:ITCoderITCoder

AWK 是一个通用脚本语言,主要设计用来对文本进行高级处理。它最常用于报告和分析工具。

和大部分程序语言不同,AWK 是数据驱动的,它意味着你可以针对输入文本进行一系列定义好的动作。它处理数据,转换数据,并且将处理结果发送到标准输出。

本文涉及 AWK 编程语言的基础知识。了解awk 这些基础知识将会很大程度上提高你在终端上操作文本的能力。

一、AWK如何工作的

awk 有很多不同的实现。我们将会使用 GNU 版本的 awk 实现,它被称为 gawk。在大多数 Linux 系统中,awk就是gawk的快捷方式。

1.1 记录和文本域

Awk 处理文本数据,不管是从文件来的或者数据流中来的。输入数据被区分为记录和文本域。 Awk 一次处理一条记录,一直到输入结束。记录通常被称为记录分隔符的字符分隔。默认的记录分隔符是换行字符,它意味着文本中的一行数据就是一条记录。一个新的记录分隔符可以通过RS变量进行设置。

记录由被文本域分隔符的文本域组成。默认情况下,文本域由一个空白字符构成,包括一个或者多个 tab,space 和 换行字符。

每条记录中的文本域以美元符号($)加上文本域编号来表示,以1开始。第一个文本域代表 $1, 第二个 $2,依此类推。最后一个文本域可以使用特殊变量$NF表示。整个记录可以标识为$0

这是一个可见标识图,显示记录和文本域如何标识:

代码语言:javascript
复制
tmpfs      788M  1.8M  786M   1% /run/lock 
/dev/sda1  234G  191G   31G  87% /
|-------|  |--|  |--|   |--| |-| |--------| 
   $1       $2    $3     $4   $5  $6 ($NF) --> fields
|-----------------------------------------| 
                    $0                     --> record

1.2 Awk 程序

想要使用awk处理文本,你需要告诉命令如何做。这个程序包含了一系列规则和用户预定义的函数。每个规则包含一个样式和一个动作。规则由换行符或者分号(;)分隔。典型的,一个 AWK 程序类似这样:

代码语言:javascript
复制
pattern { action }
pattern { action }
...

awk程序处理数据时,如果样式匹配了记录,它会在这个记录上执行指定的动作。当规则没有包含任何样式,那么所有的记录(行)都被被匹配。

一个 awk 动作由一个大括号包围,并且由表达式组成。每一个表达式指定一个可以被执行的操作。一个动作可以包含一个或者多个表达式,以换行符或者分号(;)分隔。如果规则没有动作,它默认是打印所有的记录。

Awk 支持多种不同的表达式,包括条件表达式,输入输出表达式等等。最常用的 awk 表达式是:

  • exit - 停止执行程序并退出
  • next - 停止处理当前记录并且移动到输入数据的下一条记录
  • print - 打印记录,文本域,变量 和自定义文本
  • printf - 格式化打印,类似于C 和 bash 的 printf

当写 awk 程序的时候,所有在符号#后面的内容,一直到行尾,都是注释。 很长的一行数据可以使用\符号打破成多行内容。

1.3 执行 awk 程序

一个 awk 程序可以以多种方式运行。如果程序是简单的,简短的,它可以在命令行直接传给 awk 处理。

代码语言:javascript
复制
awk 'program' input-file...

在命令行运行程序的时候,它应该以单引号('')包围,以便shell 不会解释程序。

如果程序是很大的,并且很复杂的,它最好被放进文件,并且通过使用-f选项来传递文件给awk命令:

代码语言:javascript
复制
awk -f program-file input-file...

在下面的例子中,我们将会使用一个名为"teams.txt"的文件,它看起来像下面这样:

代码语言:javascript
复制
Bucks Milwaukee    60 22 0.732 
Raptors Toronto    58 24 0.707 
76ers Philadelphia 51 31 0.622
Celtics Boston     49 33 0.598
Pacers Indiana     48 34 0.585

二、Awk 样式

awk 中的样式控制关联的动作是否被执行。

Awk 支持不同类型的样式,包括,正则表达式,关系表达式,范围表达式和指定的表达式样式。

如果一个规则没有样式,每一个输入记录都被匹配了。下面是一个包含一个动作的规则:

代码语言:javascript
复制
awk '{ print $3 }' teams.txt

这个程序将会打印每条记录的第三个文本域:

代码语言:javascript
复制
60
58
51
49
48

2.1 正则表达式样式

一个正则表达式匹配一系列字符串。Awk 正则表达式样式使用//来包裹。

代码语言:javascript
复制
/regex pattern/ { action }

大部分基础例子中都是一个简单的字符匹配或者一个字符串匹配。例如,显示每条记录中包含"0.5"的第一个文本域,你可以运行下面的命令:

代码语言:javascript
复制
awk '/0.5/ { print $1 }' teams.txt

输出:

代码语言:javascript
复制
Celtics
Pacers

样式可以是任何一种其他的正则表达式。这是一个例子,打印任何以两个或者更多数字开头的记录的第一个文本域:

代码语言:javascript
复制
awk '/^[0-9][0-9]/ { print $1 }' teams.txt

输出:

代码语言:javascript
复制
76ers

2.2 关系表达式样式

关系表达式通常被用来匹配指定文本域或者变量的内容。

默认情况下,正则表达式样式匹配记录。想要对文本域进行正则匹配,指定文本域并且使用包含的操作符(~)匹配样式。

例如,想要打印第二个文本域包含"ia"的记录的第一个文本域,你将输入:

代码语言:javascript
复制
awk '$2 ~ /ia/ { print $1 }' teams.txt

输出:

代码语言:javascript
复制
76ers
Pacers

想要匹配不包含指定样式的文本域,使用!~操作符:

代码语言:javascript
复制
awk '$2 !~ /ia/ { print $1 }' teams.txt

输出:

代码语言:javascript
复制
Bucks
Raptors
Celtics

你可以比较字符串和数字,例如大于等于,小于等于,等于等等。下面的命令打印了第三个文本域大于等于50的所有记录中的第一个文本域。

代码语言:javascript
复制
awk '$3 > 50 { print $1 }' teams.txt

输出:

代码语言:javascript
复制
Bucks
Raptors
76ers

2.3 范围表达式样式

一个范围表达式样式包含两个样式,由逗号分隔:

代码语言:javascript
复制
pattern1, pattern2

所有以第一个样式开始,直到第二个样式被匹配的内容,这些记录都被匹配到。

下面是一个例子,它将匹配包含 “Raptors” 直到 “Celtics"的所有记录,并打印出他们的第一个文本域:

代码语言:javascript
复制
awk '/Raptors/,/Celtics/ { print $1 }' teams.txt

输出:

代码语言:javascript
复制
Raptors
76ers
Celtics

这些样式可以是关系表达式。下面的命令将会打印所有记录,从第四个文本域等于31开始到第四个文本域等于33的所有记录:

代码语言:javascript
复制
awk '$4 == 31, $4 == 33 { print $0 }' teams.txt

输出:

代码语言:javascript
复制
76ers Philadelphia 51 31 0.622
Celtics Boston     49 33 0.598

范围样式不能和其他的样式表达式一起使用。

2.4 特殊的表达式样式

Awk 包含下面的特殊样式:

  • BEGIN - 被用来在处理记录之前 先执行的动作
  • END - 被用来处理记录之后 再执行的动作

这个 BEGIN 样式通常被用来设置变量,END通常用来处理数据,例如计算。

下面的例子将会打印"Start Processing.",然后打印出每条记录的第三个文本域,最后"End Processing.”

代码语言:javascript
复制
awk 'BEGIN { print "Start Processing." }; { print $3 }; END { print "End Processing." }' teams.txt

输出:

代码语言:javascript
复制
Start Processing
60
58
51
49
48
End Processing.

如果一个程序,只有一个BEGIN样式,动作将会被执行,输入不会被处理。 如果一个程序只有END样式,输入将被处理,然后动作被执行。

Gnu 版本的 awk 还包括两个特殊的样式 BEGINFILEENDFILE, 这允许你在处理文件前后执行动作。

2.5 混合样式

Awk 允许你将两种或者更多样式通过逻辑与(&&)和逻辑或(||)来进行混合使用。

这是一个使用&&操作符的例子,用来打印第三个文本域大于等于50和第四个文本域小鱼等于30的记录中的第一个文本域:

代码语言:javascript
复制
awk '$3 > 50 && $4 < 30 { print $1 }' teams.txt

输出:

代码语言:javascript
复制
76ers Philadelphia 51 31 0.622
Celtics Boston     49 33 0.598

三、内建变量

Awk 有一些内建变量,它们包含了很有用的信息,并且允许你在程序运行的时候调用。下面是一些最常用的内建变量:

  • NF - 记录中的域序号
  • NR - 当前记录的序号
  • FILENAME - 当前处理的输入文件名称
  • FS - 文本域分隔符号
  • RS - 记录分隔符号
  • OFS - 输出文本域分隔符
  • ORS - 输出记录分隔符

这是一个例子,显示如何打印文件名和行数:

代码语言:javascript
复制
awk 'END { print "File", FILENAME, "contains", NR, "lines." }' teams.txt

输出:

代码语言:javascript
复制
File teams.txt contains 5 lines.

AWK 变量可以在程序的任何一行中设置。想要在整个程序中定义一个变量,你应该在BEGIN样式中定义这个变量。

四、修改文本域分隔符和记录分隔符

默认的文本域分隔符是任意数量的空格或者 tab 符号。它可以通过FS变量来修改。

例如,想要设置.为文本域分隔符,你需要用:

代码语言:javascript
复制
awk 'BEGIN { FS = "." } { print $1 }' teams.txt

输出:

代码语言:javascript
复制
Bucks Milwaukee    60 22 0
Raptors Toronto    58 24 0
76ers Philadelphia 51 31 0
Celtics Boston     49 33 0
Pacers Indiana     48 34 0

文本域分隔符可以被设置为多个字符:

代码语言:javascript
复制
awk 'BEGIN { FS = ".." } { print $1 }' teams.txt

在命令行中运行 awk 时,你可以使用-F 选项来修改文本域分隔符:

代码语言:javascript
复制
awk -F "." '{ print $1 }' teams.txt

默认情况下,记录分隔符是一个换行符,并且可以使用RS变量修改。

这是一个例子,演示如何将记录分隔符修改为.

代码语言:javascript
复制
awk 'BEGIN { RS = "." } { print $1 }' teams.txt

输出:

代码语言:javascript
复制
Bucks Milwaukee    60 22 0
732 
Raptors Toronto    58 24 0
707 
76ers Philadelphia 51 31 0
622
Celtics Boston     49 33 0
598
Pacers Indiana     48 34 0
585

五、Awk 动作

Awk 动作被大括号({})包裹,并且在样式匹配时执行。一个动作可以有0个或者更多个表达式。多个表达式会按照它们的顺序依次执行,并且必须被换行符和分号分隔。

下面是一些在 awk 中支持的动作类型:

  • 表达式,例如变量赋值,算术操作,自增,自减操作
  • 控制表达式,被使用来控制程序的流程。(if,for,while,switch等等)
  • 输出表达式,例如printprintf
  • 混合表达式,将其他的表达式组合起来
  • 输入表达式,处理输入
  • 删除表达式,删除任何数组元素

这个print表达式是最常用的 awk 表达式,它打印出格式化的文本,记录,文本域和变量

当打印多个条目时,你需要使用逗号分隔开。下面是一个例子:

代码语言:javascript
复制
awk '{ print $1, $3, $5 }' teams.txt

被打印的条目被一个空格符分隔开:

代码语言:javascript
复制
Bucks 60 0.732
Raptors 58 0.707
76ers 51 0.622
Celtics 49 0.598
Pacers 48 0.585

如果你不使用逗号,在打印出的条目之间将没有空白:

代码语言:javascript
复制
awk '{ print $1 $3 $5 }' teams.txt

打印的条目被拼接在一起:

代码语言:javascript
复制
Bucks600.732
Raptors580.707
76ers510.622
Celtics490.598
Pacers480.585

print被不带参数使用时,它默认指向print $0。打印当前记录。

想要打印自定义文本,你需要使用双引号引用文本:

代码语言:javascript
复制
awk '{ print "The first field:", $1}' teams.txt

输出:

代码语言:javascript
复制
The first field: Bucks
The first field: Raptors
The first field: 76ers
The first field: Celtics
The first field: Pacers

你也可以打印特殊字符,例如换行符:

代码语言:javascript
复制
awk 'BEGIN { print "First line\nSecond line\nThird line" }'

输出:

代码语言:javascript
复制
First line
Second line
Third line

printf表达式给你更多输出格式的控制。这是一个例子,插入行号:

代码语言:javascript
复制
awk '{ printf "%3d. %s\n", NR, $0 }' teams.txt

printf不会在每个记录后面创建一个新的换行符,因此我们使用\n

代码语言:javascript
复制
  1. Bucks Milwaukee    60 22 0.732 
  2. Raptors Toronto    58 24 0.707 
  3. 76ers Philadelphia 51 31 0.622
  4. Celtics Boston     49 33 0.598
  5. Pacers Indiana     48 34 0.585

下面的命令计算了每一行被存储在第三个文本域的值之和:

代码语言:javascript
复制
awk '{ sum += $3 } END { printf "%d\n", sum }' teams.txt

输出:

代码语言:javascript
复制
266

这是另外一个例子,样式使用表达式和控制表达式来打印从1到5的数字的平方:

代码语言:javascript
复制
awk 'BEGIN { i = 1; while (i < 6) { print "Square of", i, "is", i*i; ++i } }'

输出:

代码语言:javascript
复制
Square of 1 is 1
Square of 2 is 4
Square of 3 is 9
Square of 4 is 16
Square of 5 is 25

类似上面的一行命令可能会难以理解和维护。在编写较长程序的时候,你需要创建一个独立的程序文件(prg.awk):

代码语言:javascript
复制
BEGIN { 
  i = 1
  while (i < 6) { 
    print "Square of", i, "is", i*i; 
    ++i 
  } 
}

通过将文件名传递给awk,运行程序:

代码语言:javascript
复制
awk -f prg.awk

你可以运行一个 awk 程序,通过使用shebang指令和设置:

代码语言:javascript
复制
#!/usr/bin/awk -f
BEGIN { 
  i = 1
  while (i < 6) { 
    print "Square of", i, "is", i*i; 
    ++i 
  } 
}

保存文件,并且使得它可执行:

代码语言:javascript
复制
chmod +x prg.awk

现在你可以通过输入下面的命令来执行这个程序:

代码语言:javascript
复制
./prg.awk

六、在 Awk 程序中使用 Shell 变量

如果你在 shell 脚本中使用awk命令,你可能需要传递 shell 变量给 awk 程序。一个 选项是使用双引号定义变量,并且在程序中替换这个变量。不管怎么样,选项将会使得你的程序更复杂,因此你需要避免使用 awk 变量。

推荐在 awk 程序中使用 shell 变量的方式是将 shell 变量赋值给一个 awk 变量。下面是一个例子:

代码语言:javascript
复制
num=51
awk -v n="$num" 'BEGIN {print n}'

输出:

代码语言:javascript
复制
51

七、总结

Awk 是最强大的文本操作工具之一。

本文只讲了 awk 编程语言的皮毛。想要学习更多关于 awk 的知识,请参阅 Gawk 官方文档

原文 :https://linuxize.com/post/awk-command/

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020年04月15日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、AWK如何工作的
    • 1.1 记录和文本域
      • 1.2 Awk 程序
        • 1.3 执行 awk 程序
        • 二、Awk 样式
          • 2.1 正则表达式样式
            • 2.2 关系表达式样式
              • 2.3 范围表达式样式
                • 2.4 特殊的表达式样式
                  • 2.5 混合样式
                  • 三、内建变量
                  • 四、修改文本域分隔符和记录分隔符
                  • 五、Awk 动作
                  • 六、在 Awk 程序中使用 Shell 变量
                  • 七、总结
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档