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|广告【讨论】:
我几乎可以肯定gensub
是 gawk
特定的功能。如果你输入awk --version
;-?),你会从你的 awk 中得到什么。祝大家好运。
我完全确定 gensub 是一种 gawk 主义,尽管 BusyBox awk 也有它。不过,这个答案也可以使用 gsub 来实现:echo 'ab cb ad' | awk 'gsub(/a./,SUBSEP"&"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:从线型访问捕获的组的主要内容,如果未能解决你的问题,请参考以下文章