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/
,意思是匹配apple
ororange
被应用一次。调用后不会发生任何事情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
在所有其他情况下使用:
- 它在替换字符串中提供了更详细的反向引用
- 如果你想保持原始变量不变
- 如果要将结果分配给变量