joe*_*epd 2 sed awk text-processing
awk
是文本处理的瑞士军刀。但是,如果我需要更改文本中的小部分,我sed
通常会联系。虽然它可能是完成这项工作的最佳工具,但了解如何使用其他工具完成如此简单的任务是值得的。我将如何awk
用作流编辑器的替代品sed
?
特别是,使用以下文件text
:
Comparing apples with oranges.
Comparing rockets with bicycles.
Run Code Online (Sandbox Code Playgroud)
如何实现以下结果awk
:
sed 's/apples/fruit/' text
sed 's/apples\|oranges/fruit/g' text
Run Code Online (Sandbox Code Playgroud)
作为奖励,我如何awk
使用这些函数更改变量?
流编辑器是一种特殊类型的过滤器。过滤器是一个程序,它接受标准输入上的文本,执行一些魔术,然后将其吐出到标准输出上。 grep
,而且基本上coreutils
都是过滤器。流编辑器是一种特殊类型的过滤器:它对传入的文本应用一个或多个编辑命令。
在 中awk
,可以使用以下三个函数:sub、gsub和gensub,概要如下:
sub(regexp, replacement [, target])
gsub(regexp, replacement [, target])
gensub(regexp, replacement, how [, target])
Run Code Online (Sandbox Code Playgroud)
在所有这三个函数中,如果target
省略,$0
则假定当前行 ( )。
我们先来看看sub
。
$ awk '{rt = sub(/apple|orange/, "fruit"); print rt, $0}' text
1 Comparing fruits with oranges.
0 Comparing rockets with bicycles.
Run Code Online (Sandbox Code Playgroud)
这里,sub()
-function的返回值存储在rt
. 正则表达式/apple|orange/
,意思是匹配apple
或orange
应用一次。调用之后什么都不会发生sub
,但是在后台,当前行发生了变化,返回值有一个值。
返回值是0
未进行任何更改时的值,这意味着如果 sub 在 之外应用{action}
,则可用于模拟sed
.
$ awk 'sub(/apple|orange/, "fruit")' text
Comparing fruits with oranges.
Run Code Online (Sandbox Code Playgroud)
现在,由于只有第一行发生了变化,因此只打印了第一行。请记住,如果未指定,则操作是打印该行。
要模拟sed 's/apple/fruit/' text
,可以这样写:
$ awk 'sub(/apple|orange/, "fruit") || 1' text
Comparing fruits with oranges.
Comparing rockets with bicycles.
Run Code Online (Sandbox Code Playgroud)
现在,将尝试第一个功能。如果某些内容已被替换,则返回值非零,并打印该行。如果没有被替换,PATTERN
则将尝试第二个测试,它恰好总是非零,即1
。结果,将打印(未修改的)行。
另一种编写相同且可能更惯用的方法是:
$ awk '{sub(/apple|orange/, "fruit")};1' text
Comparing fruits with oranges.
Comparing rockets with bicycles.
Run Code Online (Sandbox Code Playgroud)
在这里,尝试更改第一个ACTION
块中的当前行。的返回码sub
将被默默忽略。什么都不会打印。第二个PATTERN{ACTION}
-block ( 1
),始终匹配,并且默认操作 idf top 打印它,无论是修改行还是未修改行。
您已经注意到第一行的第二个匹配项orange
没有被替换。一种解决方案是将sub
-function包装在 while 循环中:
$ awk '{while (sub(/apple|orange/, "fruit")){}};1' text
Comparing fruits with fruits.
Comparing rockets with bicycles.
Run Code Online (Sandbox Code Playgroud)
只要sub
返回一个非零值, sub 就会被重复。作为一个方便的简写,并且由于 while 循环在PATTERN
a 中不起作用,gsub
因此引入了一个函数。
$ awk 'gsub(/apple|orange/, "fruit")' text
Comparing fruits with fruits.
Run Code Online (Sandbox Code Playgroud)
这意味着sed 's/regex/replacement/g'
可以awk
像这样模拟著名的:
awk '{gsub(/apple|orange/, "fruit")};1' text
Run Code Online (Sandbox Code Playgroud)
警告:
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.
Run Code Online (Sandbox Code Playgroud)
这可能不是您想要的。计算中的一个合理原则是不处理输入本身,而是处理输入的副本。如果您不想更改输入,而是将替换结果分配给新变量怎么办?输入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.
Run Code Online (Sandbox Code Playgroud)
这里,返回值不是返回值,而是将结果字符串赋给变量 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.
Run Code Online (Sandbox Code Playgroud)
如果 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.
Run Code Online (Sandbox Code Playgroud)
从 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.
Run Code Online (Sandbox Code Playgroud)
将 sub 和 gsub 用于快速而肮脏的任务:
使用gensub
在其它情况下: