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

Time:11-12

I have a file foo.properties with contents like

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

In my script, I need to replace whatever value is against ph (The current value is unknown to the bash script) and change it to 0.5. So the the file should look like

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

I know it can be easily done if the current value is known by using sed "s/\,ph\:0.03\,/\,ph\:0.5\,/" foo.properties But in my case, I have to actually read the contents against allNames and search for the value and then replace within a for loop. Rest all is taken care of but I can't figure out the sed/perl command for this. I tried using sed "s/\,ph\:.*\,/\,ph\:0.5\,/" foo.properties and some variations but it didn't work.

CodePudding user response:

With your shown samples, please try following awk code.

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

Detailed Explanation: Creating awk's variable named new_val which contains new value which needs to put in. In main program of awk using match function of awk to match ,ph:[0-9] (\.[0-9] )? regex in each line, if a match of regex is found then storing that matched value into variable val. Then substituting everything from : to till end of value in val variable with : here. Then printing values as pre requirement of OP(values before matched regex value with val(edited matched value in regex) with new value and rest of line), using next will avoid going further and by mentioning 1 printing rest other lines which are NOT having a matched value in it.



2nd solution: Using sub function of awk.

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

CodePudding user response:

Would you please try a perl solution:

perl -pe '
    s/(?<=\bph:)[\d.] (?=,|$)/0.5/;
' foo.properties
  • The -pe option makes perl to read the input line by line, perform the operation, then print it as sed does.
  • The regex (?<=\bph:) is a zero-length lookbehind which matches the string ph: preceded by a word boundary.
  • The regex [\d.] will match a decimal number.
  • The regex (?=,|$) is a zero-length lookahead which matches a comma or the end of the string.
  • As the lookbehind and the lookahead has zero length, they are not substituted by the s/../../ operator.

[Edit]
As Dave Cross comments, the lookahead (?=,|$) is unnecessary as long as the input file is correctly formatted.

CodePudding user response:

Works with decimal place or not, or no value, anywhere in the line.

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

Or possibly:

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

The top one uses "not other naming characters" to describe the character immediately before a name, the bottom one uses delimiter characters (you could add more characters to either). The purpose is to avoid clashing with other_ph or autograph.

CodePudding user response:

Here you go

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

output:

Perl Starting ...

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

Perl End ...

CodePudding user response:

A simpler sed solution:

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

Here we match ([=,]ph:) (i.e. , or = followed by ph:) and capture in group #1. This should be followed by 1 of [0-9.] character to natch any number. In replacement we put \1 back with 0.5

  • Related