使用 awk 作为流编辑器

使用 awk 作为流编辑器

awk是文本操作的瑞士军刀。但是,如果我需要更改文本中的小部分,我会进行sed一般性修改。虽然它可能是完成这项工作的最佳工具,但了解如何使用其他工具完成如此简单的任务是值得的。我将如何用作awk流编辑器的替代品sed

特别是,使用以下文件text

Comparing apples with oranges.
Comparing rockets with bicycles.

如何实现以下结果awk

sed 's/apples/fruit/' text
sed 's/apples\|oranges/fruit/g' text

awk作为奖励,我如何使用这些函数更改变量?

答案1

流编辑器是一种特殊类型的过滤器。过滤器是一个程序,它在标准输入上获取文本,执行一些魔法,然后将其输出到标准输出上。 grep,基本上都是coreutils过滤器。流编辑器是一种特殊类型的过滤器:它对传入文本应用一个或多个编辑命令。

在 中awk,可以使用以下三个函数:,格子, 和根子概要如下:

sub(regexp, replacement [, target])
gsub(regexp, replacement [, target])
gensub(regexp, replacement, how [, target])

在所有这三个函数中,如果target省略,则$0假定当前行 ( )。

子和gsub

我们首先来看看sub.

$ awk '{rt = sub(/apple|orange/, "fruit"); print rt, $0}' text 
1 Comparing fruits with oranges.
0 Comparing rockets with bicycles.

这里,函数的返回值sub()存储在 中rt。正则表达式/apple|orange/,意思是匹配appleororange被应用一次。调用后不会发生任何事情sub,但在后台,当前行已更改,并且返回值有值。

返回值是0未进行任何更改时的值,这意味着如果 sub 应用于 之外{action},则可用于模拟sed

$ awk 'sub(/apple|orange/, "fruit")' text    
Comparing fruits with oranges.

现在,由于仅第一行发生了变化,因此仅打印了第一行。请记住,如果未指定,则执行的操作是打印该行。

为了模拟sed 's/apple/fruit/' text,可以写:

$ awk 'sub(/apple|orange/, "fruit") || 1' text
Comparing fruits with oranges.
Comparing rockets with bicycles.

现在,将尝试第一个功能。如果某些内容已被替换,则返回值非零,并打印该行。如果没有任何内容被替换,PATTERN则将尝试 的第二个测试,它恰好总是非零,即1。结果,将打印(未修改的)行。

另一种编写相同且可能更惯用的方法是:

$ awk '{sub(/apple|orange/, "fruit")};1' text 
Comparing fruits with oranges.
Comparing rockets with bicycles.

这里,尝试更改第一个ACTION块中的当前行。的返回码sub将被默默地忽略。不会打印任何内容。第二个PATTERN{ACTION}- 块 ( 1),始终火柴,默认操作 idf top 打印它,无论它是修改的还是未修改的行。

您已经注意到第一行的第二个匹配项orange没有被替换。一种解决方案是将sub-function 包装在 while 循环中:

$ awk '{while (sub(/apple|orange/, "fruit")){}};1' text
Comparing fruits with fruits.
Comparing rockets with bicycles.

只要sub返回非零值,sub就会重复。作为对此的方便简写,并且由于 while 循环在 a 中不起作用,因此引入了PATTERN一个函数。gsub

$ awk 'gsub(/apple|orange/, "fruit")' text             
Comparing fruits with fruits.

这意味着可以像这样sed 's/regex/replacement/g'模仿著名的:awk

awk '{gsub(/apple|orange/, "fruit")};1' text

gensub:无副作用

警告:gensub不符合 POSIX awk 标准,并且可能在您的安装中不可用。它在gawk,中可用busybox awk,但在mawk和中不可用nawk

这些机制已经展示了一些如何使用变量的工作原理。变量就地改变了。

$ awk '{a=$0; rt=sub(/apple|orange/, "fruit", a); print rt, a, $0}' text
1 Comparing fruits with oranges. Comparing apples with oranges.
0 Comparing rockets with bicycles. Comparing rockets with bicycles.

这可能不是您想要的。计算中的一个合理原则是不处理输入本身,而是处理输入的副本。如果您不想更改输入,而是将替换结果分配给新变量怎么办?进入gensub

$ awk '{rt=gensub(/apple|orange/, "fruit", "g"); print rt, $0}' text
Comparing fruits with fruits. Comparing apples with oranges.
Comparing rockets with bicycles. Comparing rockets with bicycles.

这里,返回值不是返回值,而是将结果字符串赋值给变量rt。第四个参数现在是默认值 $0。

gensub 的第三个参数是如何。该参数的合理值是“g”或“G”,它代表全局。这将使用替换字符串更改所有出现的 /regex/ 。还可以指定一个正整数 i,其中第 i 次出现的位置将被替换。

$ gawk '{print gensub(/apple|orange/, "fruit", 1)}' text
Comparing fruits with oranges.
Comparing rockets with bicycles.

$ gawk '{print gensub(/apple|orange/, "fruit", 2)}' text
Comparing apples with fruits.
Comparing rockets with bicycles.

$ gawk '{print gensub(/apple|orange/, "fruit", 3)}' text
Comparing apples with oranges.
Comparing rockets with bicycles.

$ gawk '{print gensub(/apple|orange/, "fruit", "g")}' text
Comparing fruits with fruits.
Comparing rockets with bicycles.

如果how不是正整数,或者不是以G或g开头的字符串,gawk将发出警告。

请注意,gensub 的另一种惯用用法是:直接打印替换结果。最后一种形式也可以替代sed 's/regex/replacement/g'命令。

使用替换字符串执行更多操作

到目前为止,我们已经完成了一些直接的字符串替换。如果想修改匹配的字符串怎么办?

有一些特殊的变量可以捕获匹配的文本。使用 POSIX-conform sub 和 gsub,可以用 & 重复匹配的部分:

$ awk '{rt=gsub(/apple|orange/, "a basket of &"); print rt, $0}' text
2 Comparing a basket of apples with a basket of oranges.
0 Comparing rockets with bicycles.

sed 和 perl/PCRE 中带有编号匹配的奇特事物对于 sub 和 gsub 变体来说太现代了。 gensub 可以对 & 执行相同的操作,但当您在正则表达式中使用分组来指定正则表达式时,可以执行更多操作:

$ awk '{rt=gensub(/(appl|orang)(e)/, "a basket of \\1\\2","g"); print rt}' text
Comparing a basket of apples with a basket of oranges.
Comparing rockets with bicycles.

TL;DR

使用 sub 和 gsub 来完成快速而肮脏的任务:

  • 当你想立即更改一个变量,并且不关心它的旧值时
  • 当您希望通过使用返回代码来根据是否进行替换来执行操作时

gensub在所有其他情况下使用:

  • 它在替换字符串中提供了更详细的反向引用
  • 如果你想保持原始变量不变
  • 如果要将结果分配给变量

相关内容