如何表示awk sub或gsub的匹配字符串的多个部分。
对于像“ ## code”这样的正则表达式,如果我想在“ ##”和“ code”之间插入一个单词,我想要一种类似VSCode语法的方式,其中$ 1代表第一部分,$ 2代表第二部分部分
sub(/(##)(code)/, "$1before$2", str)
从awk的用户手册中,我发现awk使用&表示整个匹配的字符串。如何像VSCode那样表示匹配的字符串中的一个,两个或更多部分。
sub(regexp,替换[,目标]) 搜索目标,被视为字符串,用于与正则表达式regexp匹配的最左边,最长的子字符串。通过用替换替换匹配的文本来修改整个字符串。修改后的字符串成为目标的新值。返回进行替换的次数(零或一)。
regexp参数可以是regexp常量(/…/)或字符串常量(“…”)。在后一种情况下,该字符串被视为要匹配的正则表达式。有关这两种形式之间的差异以及正确编写程序的含义的讨论,请参见Compute Regexps。
该函数是特殊的,因为target不是简单地用于计算值,而且不仅仅是任何表达式都可以—它必须是变量,字段或数组元素,以便sub()可以在那里存储修改后的值。如果省略此参数,则默认值为使用和更改$ 0.48例如:
str =“水,水无处不在” sub(/ at /,“ ith”,str) 通过用“ ith”替换最长的“ at”处最长的出现,将str设置为“枯萎,无处不在”。
如果替换中出现特殊字符“&”,则表示与regexp匹配的精确子字符串。 (如果正则表达式可以匹配多个字符串,则此精确子字符串可能会有所不同。)例如:
{ sub(/candidate/, "& and his wife"); print }
在每个输入行上将首次出现的“候选人”更改为“候选人及其妻子”。这是另一个示例:
用户手册的链接为here
答案 0 :(得分:2)
您最好的选择是将GNU awk用于以下任何一项:
$ awk '{$0=gensub(/(##)(code)/,"\\1before\\2",1)} 1' <<<'##code'
##beforecode
$ awk 'match($0,/(##)(code)/,a){$0=a[1] "before" a[2]} 1' <<<'##code'
##beforecode
第一个只允许您移动文本段,而第二个只允许您调用函数,执行数学运算或对匹配的文本进行其他操作,然后再将其移动到原始文本中或对其进行其他操作:
$ awk 'match($0,/(##)(code)/,a){$0=length(a[1])*10 "before" toupper(a[2])} 1' <<<'##code'
20beforeCODE
考虑了一下之后,我不知道如何仅使用POSIX awk结构以任何合理的方式获得所需的行为。这是我尝试过的(matches()
函数):
$ cat tst.awk
BEGIN {
str = "foobar"
re = "(f.*o)(b.*r)"
printf "\nre \"%s\" matching string \"%s\"\n", re, str
print "succ: gensub(): ", gensub(re,"<\\1> <\\2>",1,str)
print "succ: match(): ", (match(str,re,a) ? "<" a[1] "> <" a[2] ">" : "")
print "succ: matches(): ", (matches(str,re,a) ? "<" a[1] "> <" a[2] ">" : "")
str = "foofoo"
re = "(f.*o)(f.*o)"
printf "\nre \"%s\" matching string \"%s\"\n", re, str
print "succ: gensub(): ", gensub(re,"<\\1> <\\2>",1,str)
print "succ: match(): ", (match(str,re,a) ? "<" a[1] "> <" a[2] ">" : "")
print "fail: matches(): ", (matches(str,re,a) ? "<" a[1] "> <" a[2] ">" : "")
}
function matches(str,re,arr, start,tgt,n,i,segs) {
delete arr
if ( start=match(str,re) ) {
tgt = substr($0,RSTART,RLENGTH)
n = split(re,segs,/[)(]+/) - 1
for (i=1; RSTART && (i < n); i++) {
if ( match(str,segs[i+1]) ) {
arr[i] = substr(str,RSTART,RLENGTH)
str = substr(str,RSTART+RLENGTH)
}
}
}
return start
}
。
$ awk -f tst.awk
re "(f.*o)(b.*r)" matching string "foobar"
succ: gensub(): <foo> <bar>
succ: match(): <foo> <bar>
succ: matches(): <foo> <bar>
re "(f.*o)(f.*o)" matching string "foofoo"
succ: gensub(): <foo> <foo>
succ: match(): <foo> <foo>
fail: matches(): <foofoo> <>
但是对于第二种情况当然不起作用,因为f.*o
的第一个RE段与整个字符串foofoo
匹配,并且如果您尝试采用RE段,当然也会发生相同的情况相反。我也考虑过像上面那样获得RE段,但是随后从传入的字符串中一次构建一个字符,并将第一个RE段与THAT进行比较,直到匹配为止,因为这将是RE段中最短的匹配字符串,但对于像这样的字符串+ RE将会失败:
str='foooobar'
re='(f.*o)(b.*r)'
因为f.*o
确实需要与foo
匹配的fooooo
。
所以-我想您需要继续进行迭代(注意迭代的方向-我期望从头到尾都是正确的),直到将字符串拆分为各个段为止,每个段都与左侧的每个RE段匹配-最长的时尚。似乎需要很多工作!
答案 1 :(得分:0)
使用GNU awk时,可以为此使用gensub
。如果没有gensub
进行任何通用awk,它将变得更加乏味。该过程可能是这样的:
ere="(ere1)(ere2)"
match(str,ere)
tmp=substr(str,RSTART,RLENGTH)
match(tmp,"ere1"); part1=substr(tmp,RSTART,RLENGTH)
part2=substr(tmp,RLENGTH)
sub(ere,part1 "before" part2,str)
此问题是,它将无法始终正常工作,您必须对其进行一些设计。由于ERE的贪婪,可以创建一个简单的失败。
str="foocode"
ere="(f.*o)(code)"
match(str,ere) # finds "foocode"
tmp=substr(str,RSTART,RLENGTH) # tmp <: "foocode"
match(tmp,"(f.*o)"); # greedy "fooco"
part1=substr(tmp,RSTART,RLENGTH) # part1 <: "fooco"
part2=substr(tmp,RLENGTH) # part2 <: "de"
sub(ere,part1 "before" part2,str) # :> "foocobeforede