【问题标题】:Perl regular expression substitution with groupsPerl 正则表达式替换与组
【发布时间】:2018-10-20 09:00:01
【问题描述】:

我有以下 JSON 输入

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

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

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

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

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

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

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

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

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

但我确信有更好、更优雅的方式来做到这一点。

【问题讨论】:

  • 更好更优雅的方法是使用 JSON 解析器...

标签: regex linux perl ksh


【解决方案1】:

Perl 不使用\1 进行替换。如果您启用了警告(例如,使用perl -w),Perl 会告诉您它是$1。可以通过添加{}来消除周围数字的歧义:

perl -pe 's|(.*time"\s*:\s*").*?(".*)|${1}20:00:00$2|g'

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

另一方面,如果您只是要自行替换它,那么匹配 .* 的意义何在?难道就这样吗

perl -pe 's|(time"\s*:\s*").*?(")|${1}20:00:00$2|g'

?

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

perl -pe 's|(time"\s*:\s*")[^"]*(")|${1}20:00:00$2|g'

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

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

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

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

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

【讨论】:

  • 这太棒了!我先尝试了 $1 但我错过了转义然后改用 \1 (现在我明白这是错误的)。没有逃避,它对我不起作用。 perl -w -pe "s|(.*time\"\s*\:\s*\").*?(\".*)|\${1}20:00:00\${2} |g"。您使用 \K 的最后一个解决方案很棒,我肯定会使用这个解决方案。非常感谢!
  • @Nir 整个问题是您在代码周围使用了双引号。如有疑问,请始终在 shell 中使用单引号(除非您想插入变量)。
【解决方案2】:

虽然您可以使用正则表达式做到这一点,但使用 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

【讨论】:

  • 那应该是-0777,而不是-0(假设你想吞下整个输入)。 -0 将输入行终止符设置为 "\0",而不是 undef
  • 另外,perl -MJSON::PP -p0777 -e '$_ = encode_json { %{decode_json $_}, time =&gt; "20:00:00" } 是为了紧凑。
  • 谢谢,我猜我的版本不支持“Can't locate JSON/PP.pm in @INC”。我没有更新或安装任何东西的权限,所以我不能使用 jq
  • 您使用的是基于 RedHat 的发行版吗?其中一些以令人讨厌的方式拆分标准 Perl 包。通常有一个perl-full 或类似名称的包可以为您提供一切。 JSON::PP 自 Perl 5.14 起已成为标准。
  • IIRC 在旧版 RedHat 上是 perl-core
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-28
  • 2013-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多