Tutorial :Why does my Perl script delete the contents of whole file when I try to edit the file?



Question:

I am running the following code to open a file (test) and edit(search and replace) it. Program seems to open the file but instead of replacing it deletes everything in the file. I am not sure why that is happening. Can anyone help me out here?

#!use/bin/perl  use strict;  use warnings;    my $line = $ARGV[0];  my $find = '\s{6}seqfile\s=\sinfile';  my $replace = '\s{6}seqfile\s=\sinfil2';    open (FILE, ">/home/shubhi/Desktop/pamlrun/test") || die "cant open file \n";  my @body = <FILE>;  foreach $line(@body)  {  (s/$find/$replace/g);  {  print FILE "$line";  }  }  close(FILE);  print "reached here\n";  exit;  


Solution:1

Your open() is opening a file handle to write to your file. Replace

open (FILE, ">/home/shubhi/Desktop/pamlrun/test") || die "cant open file \n";  

with

open (FILE, "/home/shubhi/Desktop/pamlrun/test") || die "cant open file \n";  

In the code you've posted, it immediately creates this file for output only. You should open the file, read/process the contents, and then write it out. If the file is sizable, then write to a new file as you read the old one, and replace the old one upon (successful) completion.


Solution:2

open(FILE, ">filename") opens the file in replace mode, writing over whatever there was previously.

Also, you cannot have a regexp in the substitution pattern $replace.

If I understood your intent correctly, this could be replaced with an one-liner

perl -pi -e 's/(\s{6}seqfile\s=\sinfil)e/${1}2/' /home/shubhi/Desktop/pamlrun/test  


Solution:3

It looks like you want in-place editing magic. The easiest way to get this is to use $^I with the magic of @ARGV plus <> (look for null filehandle in the I/O Operators section):

#!/usr/bin/perl    use strict;  use warnings;    my $find = qr/\s{6}seqfile\s=\sinfile/;  my $replace = '      seqfile = infil2';    @ARGV = ("/home/shubhi/Desktop/pamlrun/test");    $^I = ".bak"; #safe the old files as file.bak    while (<>) {      s/$find/$replace/g;      print;  }  

Also, given the nature of your regex, it looks like you probably want [ ] (match a space) or \t (match a tab) not \s. \s will match tabs, spaces, and other whitespace characters.

You can also use Tie::File, but it doesn't seem to provide a backup capability:

#!/usr/bin/perl    use strict;  use warnings;    use Tie::File;    my $find = qr/\s{6}seqfile\s=\sinfile/;  my $replace = '      seqfile = infil2';    tie my @lines, "Tie::File", "testdata"      or die "could not open file: $!";    for my $line (@lines) {      $line =~ s/$find/$replace/g;  }  

Of course, you could roll your own backups with File::Copy:

#!/usr/bin/perl    use strict;  use warnings;    use Tie::File;  use File::Copy;    my $find = qr/\s{6}seqfile\s=\sinfile/;  my $replace = '      seqfile = infil2';    copy "testdata", "testdata.bak"      or die "could not backup file: $!";    tie my @lines, "Tie::File", "testdata"      or die "could not open file: $!";    for my $line (@lines) {      $line =~ s/$find/$replace/g;  }  

I would also be remiss if I did not point out that this is basically a one-liner:

perl -pi.bak -e 's/\s{6}seqfile\s=\sinfile/      seqfile = infil2/' testdata  

This can be shortened further with Perl 5.10 by taking advantage of \K (zero-width positive look-behind):

perl -pi.bak -e 's/\s{6}seqfile\s=\s\Kinfile/infil2/' testdata  


Solution:4

open for write = '>' open for read = '<'


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »