kaushik9918 has asked for the wisdom of the Perl Monks concerning the following question:

Hi I have tried the following code to conditionally substitute a variable string with another variable string, but its not working. Can anyone please let me know why or point me to another similar solution already discussed before? Thanks

my $cell_name ="xyz"; while (<FILE>){ if($_ =~ /STARTING_PATTERN\s+(.*)/){ $string=$_; $sub=$1; $string =~ s/$sub/$cell_name/ ; } } close FILE;

If my <FILE> contains the line with the string "STARTING_PATTERN RRR" it should be replaced by "STARTING_PATTERN xyz". Please remember $cell_name is not always "xyz" - its a variable, I have just used "xyz" for illustration purpose here.

  • Comment on Trying to substitute a variable with another variable in a string but not working
  • Download Code

Replies are listed 'Best First'.
Re: Trying to substitute a variable with another variable in a string but not working
by toolic (Bishop) on Feb 24, 2015 at 14:40 UTC
    It works for me when I print $string:
    my $cell_name = "xyz"; while (<DATA>) { if (/STARTING_PATTERN\s+(.*)/) { $string = $_; $sub = quotemeta $1; $string =~ s/$sub/$cell_name/; print $string; } } __DATA__ STARTING_PATTERN RRR

    Output:

    STARTING_PATTERN xyz

    Perhaps your input isn't as simple as RRR. If it contains regex metacharacters, use quotemeta, as shown.

      quotemeta works fine. but the contents of the file are not modified. thanks for your time.
Re: Trying to substitute a variable with another variable in a string but not working
by poj (Abbot) on Feb 24, 2015 at 15:04 UTC
    Were you expecting your code to modify the contents of the file ?.

    poj
      yes- expecting to modify the content of the file
Re: Trying to substitute a variable with another variable in a string but not working
by RonW (Parson) on Feb 24, 2015 at 19:09 UTC

    While it is possible to modify a file in-place, it is simpler and safer to create a new file, then rename the original with a '.bak' extension, then the new file to the name of the original:

    open FILE, '<', $file; my $nfile = $file . '.new'; open OFILE, '>', $nfile; while (<FILE>){ if($_ =~ /STARTING_PATTERN\s+(.*)/){ $sub=$1; s/$sub/$cell_name/ ; } print OFILE, $_; } close FILE; close OFILE; rename $file, $file . '.bak'; rename $nfile, $file;

    Note that while (<FILE>) is just a short cut for while ($_ = readline(FILE)) so $_ contains a copy of the line as it exists in the file. Any modifications to $_ affect only $_, not the file. In your code, you made a further copy, $string, so you were modifying a copy of a copy.

    Modifying a file in-place is tricky to get right even when simply replacing existing bytes. If you are inserting or deleting bytes (which may happen when modifying characters using a variable length encoding like UTF-8), it is a lot trickier.

      Perl is supposed to make easy things easy and in place file editing should be easy shouldn't it? Consider:

      use strict; use warnings; my $cell_name = 'xyzzy'; my $match = 'STARTING_PATTERN'; @ARGV = 'delme.txt'; $^I = '.bak'; while (<>) { s/$1/$cell_name/ if /$match\s+(.*)/; print; }

      Yup, that looks easy.

      Perl is the programming world's equivalent of English

        Different definition of "in place".

        Per perlrun, setting $^I is like including the -i option on the command line. From perlrun:

        specifies that files processed by the <> construct are to be edited in-place. It does this by renaming the input file, opening the output file by the original name, and selecting that output file as the default for print() statements. The extension, if supplied, is used to modify the name of the old file to make a backup copy

        So setting $^I = '.bak' has the same effect as my code. Minor difference: My code reads the original, writes the new, then renames both files. Setting $^I causes the original to be renamed first.

        The "in place" I was referring to is, literally, modifying the existing file where it is stored1. This can be done through careful use of tell and seek. The following should work when replacing existing bytes:

        open my $fh, '<+:raw', $file; # raw so file is read/written in bytes, +not characters my $where = tell($fh); while (<$fh>) { ...; # modify 1 or more bytes seek $fh, $where, SEEK_SET; print $fh, $_; $where = tell($fh); } close $fh;

        If attempting to add or delete bytes, more work needs to be done.

        ---

        1 This ignores the possibility that the underlying storage manager/controller could write the modified blocks of the file to different locations in storage, then update the file's "block map" after the write is successfully completed.

        But what about the danger GrandFather?
      Thank you for your suggestion. Works fine now.