【问题标题】:How to find and replace a pattern string using sed/perl/awk?如何使用 sed/perl/awk 查找和替换模式字符串?
【发布时间】:2021-12-24 11:28:41
【问题描述】:

我有一个文件 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 和一些变体,但没有奏效。

【问题讨论】:

  • 0.5 是预先确定的值吗?还是我们应该使用gamma 的值(在这种情况下为.5)?还是您提到0.5gamma 恰好是一样的只是巧合?您会一直更改字段 (?) ph 的值,还是会不时更改,如果是,您能否提供有关 alNames 记录的分隔符的更多详细信息?
  • 0.5 是预先确定的。基本上这个 sed 命令是脚本的一部分。我需要做的是从类似my_script.sh --name "ph:0.5,gamma:0.7" 的命令行参数中获取值并替换为这些值。我想我可以通过循环迭代来处理其余的事情,但无法计算 sed 命令。我试过sed "s/\,ph\:.*,/\,ph\:0.5,/",但也没有用。它替换了后续值
  • 对不起,如果我对某些值的 = 拼写错误让您感到困惑。基本上这是 java 属性文件,其中键值对由 : 分隔,我需要通过 bash 脚本替换其中的一些。
  • @JavaLearner 一旦你接受了这个问题的答案,你就应该问一个关于如何做你在your comment 中描述的事情的新问题,因为正确的做法是不会的在 shell 循环中多次调用这个问题的答案,请参阅why-is-using-a-shell-loop-to-process-text-considered-bad-practice

标签: regex perl awk sed


【解决方案1】:

使用您展示的示例,请尝试关注awk 代码。

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

详细解释: 创建awk 的变量名为new_val,其中包含需要放入的新值。在awk 的主程序中使用@ awk 的 987654326@ 函数在每一行中匹配 ,ph:[0-9]+(\.[0-9]+)? 正则表达式,如果找到匹配的正则表达式,则将该匹配值存储到变量 val 中。然后在这里用: 替换从: 到val 变量中值的结尾的所有内容。然后打印值作为 OP 的先决条件(在匹配正则表达式值之前的值与 val(正则表达式中的编辑匹配值)与新值和其余行),使用 next 将避免走得更远,并提及1打印其余其他行没有匹配的值。



第二个解决方案:使用awksub函数。

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

【讨论】:

    【解决方案2】:

    给你

    #!/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 ...
    

    【讨论】:

      【解决方案3】:

      在行中的任何地方都可以使用小数位,或者没有值。

      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 发生冲突。

      【讨论】:

        【解决方案4】:

        请您尝试perl 解决方案:

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

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

        【讨论】:

        • 我认为您在正则表达式末尾使用的lookbehind 是不必要的。 [\d.] 在到达逗号或字符串末尾时将停止匹配。
        • @DaveCross 谢谢你的评论。你是绝对正确的。我可能想多了。
        【解决方案5】:

        更简单的 sed 解决方案:

        sed -E 's/([=,]ph:)[0-9.]+/\10.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+ 的 [0-9.] 字符来匹配任何数字。作为替换,我们将\1 放回0.5

        【讨论】:

          【解决方案6】:

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

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

          $ sed 's/\(,ph:\)[^,]*/\10.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:\)[^,]*/\10.5/' foo.properties
          foo=bar
          # another property
          test=true
          allNames=alpha:.02,beta:0.25,ph:0.5,delta:1.0,gamma:.5
          

          【讨论】:

            猜你喜欢
            • 2010-12-07
            • 2017-06-16
            • 2017-02-14
            • 2017-02-08
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多