带组的 Perl 正则表达式替换

Perl regular expression substitution with groups

我有以下 JSON 输入

... "somefield":"somevalue", "time":"timevalue", "anotherfield":"value" ...

在我的 KornShell (ksh) 脚本中,我希望用我的值替换时间值。所以我使用组创建了这个正则表达式,效果很好

data=`cat somefile.json`
echo $data | perl -pe "s|(.*time\"\s*\:\s*\").*?(\".*)|%TIME%|g" | another-script.sh

... "somefield":"somevalue", "time":"%TIME%", "anotherfield":"value" ...

但是...我不能使用数字作为替代,因为 Perl 使用数字来定义组..所以这个显然不起作用:

perl -pe "s|(.*time\"\s*\:\s*\").*?(\".*)|0:00:00|g"

我可以通过两步替换来克服这个问题,

perl -pe "s|(.*time\"\s*\:\s*\").*?(\".*)|%TIME%|g" | perl -pe "s|%TIME%|20:00:00|"

... "somefield":"somevalue", "time":"20:00:00", "anotherfield":"value" ...

但我相信有更好更优雅的方法来做到这一点。

虽然您可以使用正则表达式来做到这一点,但使用 the right tool

会容易得多
jq '.time="20:00:00"' somefile.json 

如果您特别希望使用 Perl,自 2011 年以来,核心 Perl 发行版已包含一个 JSON 解析器,因此您可以执行以下操作:

perl -MJSON::PP=decode_json,encode_json -0 -E '$j = decode_json(<>); $j->{time} = "20:00:00"; say encode_json($j)' somefile.json

Perl 不使用 </code> 进行替换。如果您启用了警告(例如,使用 <code>perl -w),Perl 会告诉您它是 </code>。可以通过添加 <code>{ }:

来消除周围数字的歧义
perl -pe 's|(.*time"\s*:\s*").*?(".*)|20:00:00|g'

(我还从正则表达式中删除了所有多余的反斜杠。)

另一方面,如果您只是要自己替换它,那么匹配 .* 有什么意义呢?不就是

perl -pe 's|(time"\s*:\s*").*?(")|20:00:00|g'

?

我不是 .*.*? 的忠实粉丝。如果你试图匹配引用字符串的内部,最好具体一点:

perl -pe 's|(time"\s*:\s*")[^"]*(")|20:00:00|g'

我们没有尝试验证输入字符串,所以现在真的没有理由去匹配最终的 "(并自行替换它):

perl -pe 's|(time"\s*:\s*")[^"]*|20:00:00|g'

如果你的 Perl 不是古老的 (5.10+),你可以使用 \K 来“保留”字符串的前导部分,即不将其包含在匹配中:

perl -pe 's|time"\s*:\s*"\K[^"]*|20:00:00|g'

现在只有 [^"]* 部分会被替换,使我们不必进行任何捕获。