如何使用 sed/perl/awk 查找和替换模式字符串?

How to find and replace a pattern string using sed/perl/awk?

我有一个文件foo.properties,其内容类似于

foo=bar
# another property
test=true
allNames=alpha:.02,beta:0.25,ph:0.03,delta:1.0,gamma:.5

在我的脚本中,我需要替换 ph 的任何值(bash 脚本不知道当前值)并将其更改为 0.5。所以文件应该看起来像

foo=bar
# another property
test=true
allNames=alpha:.02,beta:0.25,ph:0.5,delta:1.0,gamma:.5

我知道如果通过使用知道当前值就可以轻松完成 sed "s/\,ph\:0.03\,/\,ph\:0.5\,/" foo.properties 但就我而言,我必须实际阅读 allNames 的内容并搜索值,然后在 for 循环中替换。其余的都已处理,但我无法为此弄清楚 sed/perl 命令。 我尝试使用 sed "s/\,ph\:.*\,/\,ph\:0.5\,/" foo.properties 和一些变体,但没有用。

使用您显示的示例,请尝试以下 awk 代码。

awk -v new_val="0.5" '
match([=10=],/,ph:[0-9]+(\.[0-9]+)?/){
  val=substr([=10=],RSTART+1,RLENGTH-1)
  sub(/:.*/,":",val)
  print substr([=10=],1,RSTART) val new_val substr([=10=],RSTART+RLENGTH)
  next
}
1
'  Input_file

详细说明: 创建 awk 的名为 new_val 的变量,其中包含需要的新值在awk的主程序中使用awkmatch函数在每一行匹配,ph:[0-9]+(\.[0-9]+)?正则表达式,如果找到正则表达式的匹配则将匹配的值存储到变量 val。然后在此处用 : 替换 val 变量中从 : 到值结尾的所有内容。然后打印值作为 OP 的预先要求(匹配正则表达式值之前的值与 val(在正则表达式中编辑的匹配值)与新值和行的其余部分),使用 next 将避免进一步并通过提及 1 打印其余行其中没有匹配的值。



第二种解决方案:使用awk.

sub函数
awk -v newVal="0.5" '/^allNames=/{sub(/,ph:[^,]*/,",ph:"newVal)} 1' Input_file

给你

#!/usr/bin/perl

use strict;
use warnings;

print "\nPerl Starting ... \n\n"; 

while (my $recordLine =<DATA>) 
{
    chomp($recordLine);

    if (index($recordLine, "ph:") != -1) 
    {
       
        $recordLine =~ s/ph:.*?,/ph:0.5,/g; 
        print "recordLine: $recordLine ...\n";

    }
}

print "\nPerl End ... \n\n"; 

__DATA__
foo=bar
# another property
test=true
allNames=alpha:.02,beta:0.25,ph:0.03,delta:1.0,gamma:.5

输出:

Perl Starting ...

recordLine: allNames=alpha:.02,beta:0.25,ph:0.5,delta:1.0,gamma:.5 ...

Perl End ...

行中的任何位置是否有小数位,或没有值。

sed -E 's/(^|[^-_[:alnum:]])ph:[0-9]*(.[0-9]+)?/ph:0.5/g'

或者可能:

sed -E 's/(^|[=,[:space:]])ph:[0-9]+(.[0-9]+)?/ph:0.5/g'

顶部使用“不是其他命名字符”来描述紧接在名称之前的字符,底部使用定界符(您可以向其中任何一个添加更多字符)。目的是避免与 other_phautograph.

冲突

请您尝试一个 perl 解决方案:

perl -pe '
    s/(?<=\bph:)[\d.]+(?=,|$)/0.5/;
' foo.properties
  • -pe选项使perl逐行读取输入,执行 操作,然后像 sed 那样打印它。
  • 正则表达式 (?<=\bph:) 是一个零长度的 lookbehind,它匹配 字符串 ph: 前面有单词边界。
  • 正则表达式 [\d.]+ 将匹配十进制数。
  • 正则表达式 (?=,|$) 是一个零长度先行匹配 逗号或字符串结尾。
  • 由于lookbehind和lookahead的长度为零,所以它们不是 由 s/../../ 运算符替换。

[编辑]
正如 Dave Cross 评论的那样,只要输入文件格式正确,就不需要先行 (?=,|$)

更简单的 sed 解决方案:

sed -E 's/([=,]ph:)[0-9.]+/.5/g' file

foo=bar
# another property
test=true
allNames=alpha:.02,beta:0.25,ph:0.5,delta:1.0,gamma:.5

这里我们匹配 ([=,]ph:)(即 ,= 后跟 ph:)并在第 1 组中捕获。这后面应该跟 1+ of [0-9.] 字符来匹配任何数字。作为替代,我们将 </code> 放回 <code>0.5

在每个 Unix 机器上的任何 shell 中使用任何 sed(发布的其他使用 sed -E 的 sed 解决方案需要 GNU 或 BSD seds):

a) 如果 ph: 永远不是 allNames 列表中的第一个标签(如您的示例输入所示):

$ sed 's/\(,ph:\)[^,]*/.5/' foo.properties
foo=bar
# another property
test=true
allNames=alpha:.02,beta:0.25,ph:0.5,delta:1.0,gamma:.5

b) 或者如果可以先:

$ sed 's/\([,=]ph:\)[^,]*/.5/' foo.properties
foo=bar
# another property
test=true
allNames=alpha:.02,beta:0.25,ph:0.5,delta:1.0,gamma:.5