tags:

views:

1388

answers:

3

I know this kind of questions have been asked already many times before. The reason why I come here again is that I feel like I've missed something simple and fundamental.

Is it possible to make this kind of search-replace routine better. For example without opening same file twice. Also speed related advices are welcome.

Please notice that this works with multiline matches and replaces also multiline strings.

#!/bin/perl -w -0777

local $/ = undef;

open INFILE, $full_file_path or die "Could not open file. $!";
$string =  <INFILE>;
close INFILE;

$string =~ s/START.*STOP/$replace_string/sm;

open OUTFILE, ">", $full_file_path or die "Could not open file. $!";
print OUTFILE ($string);
close OUTFILE;
+5  A: 

This kind of search and replace can be accomplished with a one-liner such as -

perl -i -pe 's/START.*STOP/replace_string/g' file_to_change

For more ways to accomplish the same thing check out this thread. To handle multi-line searches use the following command -

perl -i -pe 'BEGIN{undef $/;} s/START.*STOP/replace_string/smg' file_to_change

In order to convert the following code from a one-liner to a perl program have a look at the perlrun documentation.

If you really find the need to convert this into a working program then just let Perl handle the file opening/closing for you.

#!/usr/bin/perl -pi
#multi-line in place substitute - subs.pl
use strict;
use warnings;

BEGIN {undef $/;}

s/START.*STOP/replace_string/smg;

You can then call the script with the filename as the first argument

$perl subs.pl file_to_change

If you want a more meatier script where you get to handle the file open/close operations(don't we love all those 'die' statements) then have a look at the example in perlrun under the -i[extension] switch.

muteW
How do you convert this one liner to actual perl code? Does it get ugly?
And this doesn't seem to work with multiline matching
That's right, because this works on each line on its own.
Nathan Fellman
Check the edit, the BEGIN block now ensures that this works on multi-line matches too.
muteW
Alright, can it be written as perl code (not in one-liner)? I want to know that what happens to file opening/writing routines.
regexp /START.*STOP/smg will not match more than once.
Hynek -Pichi- Vychodil
Hynek, by not match more than "once" I assume you state that because of the greedy * operator. I left it in just so that once the OP realises the need for *? he/she will have the /g extension ready.
muteW
+1  A: 

Considering that you slurp in the whole contents of the file with:

local $/ = undef;

open INFILE, $full_file_path or die "Could not open file. $!";
$string =  <INFILE>;
close INFILE;

And then do all the processing with $string, there's no connection between how you handle the file and how you process the contents. You'd have an issue if you opened the file for writing before you were done reading it, since opening a file for writing creates a new file, discarding the previous contents.

If all you're trying to do is save on open/close statements, then do as Jonathan Leffer suggested. If your question is about multiline search and replace, then please clarify what the problem is.

Nathan Fellman
It's about generic multiline search and replace. Is it really fine that I open the same file pointer again even if the file is very huge? In one-liners there seem to be no need to open same file twice. I'm still missing something here. Maybe I should see Jonathan's example in practice.
creating a file handler has nothing to do with the file's size. It's just a pointer. The act of opening a file doesn't imply reading its contents.
Nathan Fellman
I think this is somewhere near my misunderstanding. How to open the same file once for both reading and writing when reading means the necessary operation of going it through to find possible matches?
you have to read it once and only once. When you open it for writing you're not reading it at all. It doesn't matter how big the file was before you opened it for writing because you're discarding all of that anyway.
Nathan Fellman
A: 

If you are going to slurp the file, I recommend File::Slurp.

#!/usr/bin/perl

use strict;
use warnings;

my $file_contents = read_file $full_file_path;

# As far as I can see, you do not use the m switch
$file_contents =~ s/START.*STOP/$replace_string/s;

write_file $full_file_path, $file_contents;

__END__

read_file and write_file will croak when something goes wrong.

Sinan Ünür