AWK:从线型访问捕获的组

Posted

技术标签:

【中文标题】AWK:从线型访问捕获的组【英文标题】:AWK: Access captured group from line pattern 【发布时间】:2011-02-26 19:29:56 【问题描述】:

如果我有一个 awk 命令

pattern  ... 

并且pattern使用了一个捕获组,我如何访问块中捕获的字符串?

【问题讨论】:

***.com/questions/1555173/… 有时(在简单的情况下)可以调整字段分隔符 (FS) 并选择要与 $field 匹配的内容。对输入进行预格式化也会有所帮助。 在重复的问题上有一个better answer。 Samuel Edwin Ward:这也是一个不错的答案!但它也需要gawk(因为它使用gensub)。 不用说,如果您进行简单的转换,sed 会很自然地处理捕获组。 【参考方案1】:

使用 gawk,您可以使用 match 函数来捕获带括号的组。

gawk 'match($0, pattern, ary) print ary[1]' 

示例:

echo "abcdef" | gawk 'match($0, /b(.*)e/, a) print a[1]' 

输出cd

注意 gawk 的具体用法,它实现了相关功能。

对于便携式替代方案,您可以使用 match()substr 获得类似的结果。

示例:

echo "abcdef" | awk 'match($0, /b[^e]*/) print substr($0, RSTART+1, RLENGTH-1)'

输出cd

【讨论】:

是的,gxxx 变体有很多额外的 GNU 优点和功能。 也可以在 BusyBox awk 中使用。【参考方案2】:

那是在记忆中漫步……

我很久以前就用 perl 代替了 awk。

显然 AWK 正则表达式引擎没有捕获它的组。

你可以考虑使用类似的东西:

perl -n -e'/test(\d+)/ && print $1'

-n 标志使 perl 像 awk 一样遍历每一行。

【讨论】:

显然有人不同意。此网页来自 2005 年:tek-tips.com/faqs.cfm?fid=5674 它确认您不能在 awk 中重复使用匹配的组。 对于几乎所有用例,我更喜欢 'perl -n -p -e...' 而不是 awk,因为在我看来,它更灵活、更强大并且语法更合理。 gawk != awk。它们是不同的工具,gawk 在大多数地方默认不可用。 OP 专门要求提供 awk 解决方案,所以我认为这不是答案。 @Joppe 如果没有解决方案,您将无法提供 awk 解决方案。在第 3 行中,我解释说 AWK 不支持捕获组,并且我提供了一个替代方案,OP 显然对此表示赞赏,因为该答案已被接受。我怎样才能更好地回答这个问题?【参考方案3】:

这是我一直需要的东西,所以我为它创建了一个 bash 函数。它基于格伦杰克曼的回答。

定义

将此添加到您的 .bash_profile 等中。

function regex  gawk 'match($0,/'$1'/, ary) print ary['$2:-'0'']'; 

用法

为文件中的每一行捕获正则表达式

$ cat filename | regex '.*'

为文件中的每一行捕获第一个正则表达式捕获组

$ cat filename | regex '(.*)' 1

【讨论】:

与使用grep -o有什么不同? @bfontaine grep -o 可以输出捕获的组吗? @OlleHärstedt 不,它不能。它仅涵盖您没有捕获组时的用例。在这种情况下,链式grep -o 会变得丑陋。 这需要支持多个捕获【参考方案4】:

你可以使用 GNU awk:

$ cat hta
RewriteCond %HTTP_HOST !^www\.mysite\.net$
RewriteRule (.*) http://www.mysite.net/$1 [R=301,L]

$ gawk 'match($0, /.*(http.*?)\$/, m)  print m[1]; ' < hta
http://www.mysite.net/

【讨论】:

那是what glenn jackman's answer says,差不多。 Ed Morton:这值得我说一个***的答案。编辑:嗯...为我打印RewriteRule (.*) http://www.mysite.net/$,这不仅仅是子组。 Looks like RSTART and RLENGTH refer to the substring matched by the pattern @EdMorton - 不,这将选择包含http... 模式的整行 @KFL 你是对的,但实际上有一个更糟糕的问题是发布的答案(以及我的建议使其不是特定于 gawk 的)都包含 .*? 这是 PCRE 主义和未定义的行为在 ERE 中。我会删除我的评论。【参考方案5】:

您也可以在 vanilla awk 中模拟捕获,无需扩展。但它并不直观:

步骤 1. 使用 gensub 将匹配项包围在字符串中未出现的某些字符。 步骤 2. 对角色使用 split 。 第 3 步。拆分数组中的每个其他元素都是您的捕获组。

$ echo 'ab cb ad' | awk ' split(gensub(/a./,SUBSEP"&"​​SUBSEP,"g",$0),cap,SUBSEP);打印帽[2]"|"帽[4]; ' ab|广告

【讨论】:

我几乎可以肯定 gensubgawk 特定的功能。如果你输入awk --version ;-?),你会从你的 awk 中得到什么。祝大家好运。 我完全确定 gensub 是一种 gawk 主义,尽管 BusyBox awk 也有它。不过,这个答案也可以使用 gsub 来实现:echo 'ab cb ad' | awk 'gsub(/a./,SUBSEP"&amp;"SUBSEP);split($0,cap,SUBSEP);print cap[2]"|"cap[4]' gensub() 是一个 gawk 扩展,gawk 的手册清楚地说明了这一点。其他 awk 变体也可能实现它,但它仍然不是 POSIX。试试 gawk --posix 'gsub(...)' 它会抱怨 @MestreLion,你的意思是它会抱怨gawk --posix 'gensub(...)' 尽管您对具有 gensub 函数的 POSIX awk 有误,但您的示例适用于非常有限的场景:整个模式已分组,它无法匹配某些内容当我只想提取 value 部分时,就像所有 key=(value) 一样。【参考方案6】:

我在想出一个包含 Peter Tillemans 答案的 bash 函数时有点挣扎,但这是我想出的:

函数正则表达式 perl -n -e "/$1/ && printf \"%s\n\", "'$1'

对于以下正则表达式参数,我发现这比 opsb 的基于 awk 的 bash 函数效果更好,因为我不希望打印“ms”。

'([0-9]*)ms$'

【讨论】:

我更喜欢这个解决方案,因为您可以看到组中划定捕获的部分,同时也可以忽略它们。但是,有人可以解释这是如何工作的吗?我无法让这种 perl 语法在 BASH 中正常工作,因为我不太了解它 - 尤其是 $1 周围的双/单引号标记 这不是我之前或之后做过的事情,但回顾它所做的是连接两个字符串,第一个字符串在双引号中(第一个字符串包含用反斜杠转义的嵌入双引号)第二个字符串用单引号括起来。然后将该连接的结果作为参数提供给 perl -e。您还需要知道第一个 $1(双引号内的那个)被函数的第一个参数替换,而第二个 $1(单引号内的那个)保持不变。见this example 我明白了,现在这更有意义了。那么 perl 命令中的正则表达式匹配/组捕获定义在哪里?我看到你写了'([0-9]*)ms$' - 是作为参数提供的(而字符串是另一个参数)?然后perl -e 的输出被插入到bash 的printf 命令中,以替换%s,对吗?谢谢,我希望使用这个。 您将用单引号括起来的正则表达式作为唯一参数传递给正则表达式 bash 函数。 Example 我投了反对票,因为这个问题是关于 awk 的,所以这个答案是无关紧要的。【参考方案7】:

我认为 gawk match()-to-array 仅适用于捕获组的第一个实例。

如果您想要捕获多个事物,并对它们执行任何复杂的操作,也许

gawk 'BEGIN  S = SUBSEP 
            
              nx=split(gensub(/(..(..)..(..))/, 
                              "\\1"(S)"\\2"(S)"\\3", "g", str), 
                       arr, S)
              for(x in nx)  perform-ops-over arr[x]  '

这样您就不会受到gensub()match() 的限制,这会限制您的修改的复杂性。

通过纯粹的反复试验,我注意到关于 unicode 模式下的 gawk 的一个警告:对于一个有效的 unicode 字符串 뀇꿬 以及下面列出的 6 个八进制代码:

场景 1:匹配单个字节没问题,但也会向您报告 1 的多字节 RSTART 而不是 2 的字节级答案。它也不会提供有关是否 \207 是第一个连续字节,或者是第二个字节,因为 RLENGTH 在这里总是为 1。

$ gawk 'BEGIN print match("\353\200\207\352\277\254", "\207") ' 
$ 1 

场景 2:Match 也适用于像这样的 unicode-invalid 模式

$ gawk 'BEGIN match("\353\200\207\352\277\254", "\207\352"); 
$                print RSTART, RLENGTH ' 
$ 1 2

场景 3:您可以检查是否存在针对 unicode 非法字符串的模式(\300 \xC0 对于所有可能的字节对都是 UTF8 无效的)

$ gawk 'BEGIN print ("\300\353\200\207\352\277\254" ~ /\200/) ' 
$ 1

场景 4/5/6:错误消息将显示在 (a) match() 的 unicode-invalid 字符串,index() 的任一参数为 unicode-invalid/incomplete。

$ gawk 'BEGIN match("\300\353\200\207\352\277\254", "\207\352"); print RSTART, RLENGTH ' gawk: cmd. line:1: warning: Invalid multibyte data detected. There may be a mismatch between your data and your locale. 2 2

$ gawk 'BEGIN print index("\353\200\207\352\277\254", "\352") ' gawk: cmd. line:1: warning: Invalid multibyte data detected. There may be a mismatch between your data and your locale. 0

$ gawk 'BEGIN print index("\353\200\207\352\277\254", "\200") ' gawk: cmd. line:1: warning: Invalid multibyte data detected. There may be a mismatch between your data and your locale. 0

【讨论】:

以上是关于AWK:从线型访问捕获的组的主要内容,如果未能解决你的问题,请参考以下文章

如何仅替换捕获的组?

Lua gmatch 将捕获的组存储为数组

在正则表达式的可选部分中捕获的组

如何使用 python 正则表达式替换使用捕获的组? [复制]

MatchData 中捕获的组的即时或隐式类型转换

java正则表达式匹配IP地址和端口号作为捕获的组