我需要在文本文件中搜索字符串,并进行替换,其中包含一个随每次匹配而递增的数字。
要“找到”的字符串可以是单个字符,也可以是单词或短语。
替换表达式并不总是相同(如下面的示例中所示),但总是包含一个递增的数字(变量)。
例如:
1)我有一个名为“data.txt”的测试文件。该文件包含:
Now is the time
for all good men
to come to the
aid of their party.
2)我将awk脚本放在名为“cmd.awk”的文件中。该文件包含:
/f/ {sub ("f","f(" ++j ")")}1
3)我使用这样的awk:
awk -f cmd.awk data.txt
在这种情况下,输出符合预期:
Now is the time
f(1)or all good men
to come to the
aid of(2) their party.
如果一条线上有多个匹配项,则会出现问题。例如,如果我正在搜索字母“i”,如:
/i/ {sub ("i","i(" ++j ")")}1
输出结果为:
Now i(1)s the time
for all good men
to come to the
ai(2)d of their party.
这是错误的,因为它不包括“时间”或“他们的”中的“i”。
所以,我尝试了“gsub”而不是“sub”,如:
/i/ {gsub ("i","i(" ++j ")")}1
输出结果为:
Now i(1)s the ti(1)me
for all good men
to come to the
ai(2)d of thei(2)r party.
现在它替换所有出现的字母“i”,但插入的数字对于同一行的所有匹配都是相同的。
所需的输出应为:
Now i(1)s the ti(2)me
for all good men
to come to the
ai(3)d of thei(4)r party.
注意:该数字并不总是以“1”开头,所以我可能会使用这样的awk:
awk -f cmd.awk -v j=26 data.txt
获得输出:
Now i(27)s the ti(28)me
for all good men
to come to the
ai(29)d of thei(30)r party.
为了清楚起见,替换中的数字并不总是在括号内。并且替换不会总是包含匹配的字符串(实际上它很少见)。
我遇到的另一个问题是......
我想为“搜索字符串”使用awk变量(不是环境变量),所以我可以在awk命令行中指定它。
例如:
1)我将awk脚本放在名为“cmd.awk”的文件中。该文件包含以下内容:
/??a??/ {gsub (a,a "(" ++j ")")}1
2)我会像这样使用awk:
awk -f cmd.awk -v a=i data.txt
获得输出:
Now i(1)s the ti(2)me
for all good men
to come to the
ai(3)d of thei(4)r party.
这里的问题是,如何在/ search / expression中表示变量“a”?
答案 0 :(得分:2)
awk版本:
awk '{for(i=2; i<=NF; i++)$i="(" ++k ")" $i}1' FS=i OFS=i
答案 1 :(得分:2)
gensub()
听起来很理想,它允许你替换第N个匹配,所以听起来像解决方案是在do{}while()
循环中迭代字符串,一次替换一个匹配并递增{ {1}}。如果替换不包含原始文本(或者更糟,包含多次),这种简单的j
方法将无效,请参见下文。
所以在awk中,缺少perl的“gensub()
”评估功能及其有状态正则表达式s///e
修饰符(由Steve使用),剩下的最佳选择是将行分成块(抬头,匹配,尾巴)并再次将它们重新组合在一起:
/g
这使用BEGIN {
if (j=="") j=1
if (a=="") a="f"
}
match($0,a) {
str=$0; newstr=""
do {
newstr=newstr substr(str,1,RSTART-1) # head
mm=substr(str,RSTART,RLENGTH) # extract match
sub(a,a"("j++")",mm) # replace
newstr=newstr mm
str=substr(str,RSTART+RLENGTH) # tail
} while (match(str,a))
$0=newstr str
}
{print}
作为epxression而不是match()
模式,因此您可以使用变量。 (您也可以使用“//
”,但此代码中使用了($0 ~ a) { ... }
的结果,因此请勿在此处尝试。)
您可以在命令行中定义match()
和j
。
a
支持gawk
,相当于perlre的\y
,并支持\b
和\<
明确匹配单词的开头和结尾,请注意从unix命令行添加额外的转义(我不太确定Windows可能需要或允许的内容)。
<小时/> 受限
\>
版本
如上所述:
gensub()
这里的问题是:
match($0,a) {
idx=1; str=$0
do {
prev=str
str=gensub(a,a"(" j ")",idx++,prev)
} while (str!=prev && j++)
$0=str
}
”或“i
”替换子字符串“k
”,那么下一场比赛的k(1)
索引将会被1关闭。如果您事先知道,或者通过字符串向后工作,可以解决这个问题。gensub()
”或“i
”替换子串“ii
”,则会出现类似的问题(导致无限循环,因为ii(i)
保持不变找到新的比赛)强有力地处理这两个条件并不值得代码。
答案 2 :(得分:1)
我不是说使用awk
无法做到这一点,但我强烈建议转向使用更强大的语言。请改用perl
。
要包含从26开始的字母i
的计数,请尝试:
perl -spe 's:i:$&."(".++$x.")":ge' -- -x=26 data.txt
这也可以是shell var:
var=26
perl -spe 's:i:$&."(".++$x.")":ge' -- -x=$var data.txt
结果:
Now i(27)s the ti(28)me
for all good men
to come to the
ai(29)d of thei(30)r party.
要包含特定单词的计数,请在单词周围添加单词边界(例如\b
),尝试:
perl -spe 's:\bthe\b:$&."(".++$x.")":ge' -- -x=5 data.txt
结果:
Now is the(6) time
for all good men
to come to the(7)
aid of their party.