替换字符串模式的最短匹配

替换字符串模式的最短匹配

我有这个字符串:

update mytable set mycol=myvalue where mycol=yourvalue;

我需要将其转换为:

insert into mytemp select * from mytable where mycol=youvalue;

我可以像这样完成它并且效果很好:

sed -e 's/^Update.*where//ig' -e "s/^/insert into mytemp select * from mytable where  /g" n.txt

:

如果字符串是:

update mytable set mycol=myvalue where mycol=(select yourcol from yourtable where youcol=yourvalue);

我得到:

insert into mytemp select * from mytable where youcol=yourvalue);

而我想要:

insert into mytemp select * from mytable where mycol=(select yourcol from yourtable where youcol=yourvalue);

我能做些什么?

答案1

默认情况下sed, 的正则表达式引擎是贪婪的。这意味着模式始终匹配最长的可能匹配。您必须进行非贪婪搜索,但我认为 sed 不支持非贪婪搜索。因此,您必须向搜索模式添加一个枢轴点,以便sed找到最短的可能匹配。

以下行尝试模拟特殊情况的非贪婪匹配,并且不要求通用性,因为第一个和第一个w之间的单个会使模式无效:updatewhere

sed -e 's/^Update[^w]*where//ig'\
    -e "s/^/insert into mytemp select * from mytable where  /g" n.txt

其他正则表达式引擎支持此功能,例如perl和之一awk

但就你而言,我认为这样的表达

sed -e 's/^Update.\+where\(.\+where.*\)$/\
insert into mytemp select * from mytable where \1/ig'  n.txt

对于您的具体问题会更方便。

(上面几行中的尾部\只是为了使行更清晰而添加的。)

答案2

正则表达式匹配从左到右进行,优先考虑最长的匹配。因此匹配该行^Update.*where最后出现的。where

进行这种匹配的一种方法是使用非贪婪量词*。 Sed 不支持非贪婪量词,但 Perl 支持。

perl -pe 's/^update.*?where//i; s/^/insert into mytemp select .*? from mytable where /'

另一种可能与您的数据匹配或不匹配的方法是拒绝表名和列设置中的括号。

sed -e 's/^update[^()]*where//i' -e 's/^/insert into mytemp select [^()]* from mytable where /'

更复杂的方法是首先where用唯一的标记替换第一个,然后进行替换,最后将标记恢复为where.由于 sed 逐行运行,因此保证一行不包含换行符,由\nin sed 表示。

sed -e 's/ where /\n/' \
    -e 's/^update.*$//i' -e 's/^/insert into mytemp select .* from mytable where /' \
    -e 's/\n/ where/'

相关内容