views:

535

answers:

4

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;
+5  A: 

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.

Brian Agnew
+1  A: 

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

+6  A: 

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
laalto
+4  A: 

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
Chas. Owens
I find Tie::File easier.
Svante