awk不能在一行中获取多个匹配项

3

AWK有match(s,r [,a])函数,根据手册可以将所有出现的模式记录到数组“a”中:

...如果提供了数组a,则会清除a,然后用与r相对应的子表达式匹配的s的部分填充1到n的元素。 a的第0个元素包含由整个正则表达式r匹配的字符串s的部分。下标a [n,“start”]和a [n,“length”]分别提供EACH匹配子字符串的起始索引和长度。

我期望以下行:

echo 123412341234 | awk '{match($0,"1",arr); print arr[0] arr[1] arr[2];)'

打印111

但事实上,“match”忽略了除第一个以外的所有匹配。

请问有谁能告诉我在此处使用正确的语法以用“1”的所有出现填充“arr”?


GNU的awk能够将多个匹配结果存储在一个数组中,这取决于正则表达式定义的多个匹配模式(即,括号内的项);请参见GNU awk 字符串函数match()函数的相关部分;特别是要注意讨论的后半部分,其中示例在单个regex中有2个带括号的模式:gawk '{ match($0, /(fo+).+(bar*)/, arr),这反过来将使用f0+bar*的匹配结果填充arr[]数组。 - markp-fuso
要在一行中查找多个匹配项,可以使用match()函数,但需要在正则表达式中考虑到它们(例如,在正则表达式中添加3个括号模式以匹配3个字符串副本),或编写一个循环,在行的连续块上执行match()函数。 - markp-fuso
4个回答

5

match只能找到第一个匹配项并停止搜索。你需要在循环中运行match或者使用以下方法,在任何不是1的字符上拆分输入:

echo '123412341234' | awk -F '[^1]+' '{print $1 $2 $3}'

111

或者在GNU awk中使用split

echo '123412341234' | awk 'split($0, a, /1/, m) {print m[1] m[2] m[3]}'

111

那么看起来手册是误导的。有没有办法纠正它? - krop
手册并不完全错误,因为它涵盖了匹配的全部上下文。当你的正则表达式只是/1/时,你将得到所有符合条件的结果。 - anubhava
什么是/1/?有没有扩展它的方法? - krop
1
很明显,如果你想快速获取所有匹配项,match 函数显然不是正确的选择。split 函数肯定可以做到,甚至 patsplit 也可以。 - anubhava
Split似乎不太合适,它使用正则表达式来描述分隔符,而patsplit使用正则表达式来描述子字符串。 - krop
显示剩余2条评论

4

我将利用GNU AWKpatsplit函数来完成这项任务,做法如下,假设file.txt的内容为:

123412341234

然后
awk '{patsplit($0,arr,"1");print arr[1] arr[2] arr[3]}' file.txt

提供输出

111

解释: patsplit 是一个函数,它允许您获得类似于使用 FPAT 变量的效果。它会将找到的第三个参数的所有匹配项放入提供的数组中作为第二个参数(如果不为空,则清空该数组),这些匹配项是在提供的字符串中找到的。请注意,第一个发现会放在键 1 下面,第二个发现放在 2 下面,第三个发现放在 3 下面,依此类推(在键 0 下面没有任何内容)

(在 GNU Awk 5.0.1 中测试过)


2
如果允许使用sub,那么你可以在这里进行替换。尝试运行以下awk代码。
awk '{gsub(/[^1]+/,"")} 1'  Input_file

0

patsplit() 基本上与在分割之前使用自定义的 SEPs 对所需的 regex 模式进行包装是相同的,这就是 anysplit() 在此处模拟的内容,同时还支持 UTF-8

 echo "123\uC350abc:\uF8FF:|\U1F921#xyz" | 

mawk2x '{ print ("\t\f"($0)"\n")>>(STDERR)
    
          anysplit($_, reFLEX_UCode8 "|[[-_!-/3-?]",___=2,__)
          OFS="\t"

          for(_ in __) { if (!(_%___)) { 

              printf(" matched_items[ %2d ] = # %-2d = \42%s\42\n",
                                                     _,_/___,__[_]) 
           
          } } } END { printf(ORS) }'
    
        123썐abc::|#xyz

 matched_items[  2 ] = # 1  = "3썐"
 matched_items[  4 ] = # 2  = "::"
 matched_items[  6 ] = # 3  = "#"

在后台中,anysplit() 也并不是很复杂:

xs3pFS 是一个由 \301\032\365 组成的三字节字符串,我认为即使在二进制数据中也极少出现。

gsub(patRE, xs3pFS ((pat=="&")?"\\":"") "&" xs3pFS,_)

gsub(xs3pFS "("xs3pFS")+", "",_)

return split(_, ar8, xs3pFS)

通过以这种方式拆分输入字符串,所有所需的项目将存在偶数索引中,而其余字符串将分布在奇数索引中,与gawk的split()和patsplit()中的第二个数组即第四个参数相似。但不同之处在于匹配和seps(无论您希望如何查看它们)都在同一个数组中。
当您打印出数组中的每个单元格时,您将看到:
        _SEPS_[  1 ] = # 1  = "123"
 matched_items
              [  2 ] = # 1  = "썐"

        _SEPS_[  3 ] = # 2  = "abc"
 matched_items
              [  4 ] = # 2  = "::"

        _SEPS_[  5 ] = # 3  = "|"
 matched_items
              [  6 ] = # 3  = "#"

        _SEPS_[  7 ] = # 4  = "xyz"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接