jonah_w
jonah_w

Reputation: 1032

Perl in-place substitution

In Perl one liner, we can use the -i argument to do an in-place substitution. What's the equivalence of -i when writing perl code in the IDE?

Consider the following code:

binmode(STDOUT, ':raw');
open my $fh, '<', $filename;
while (<$fh>) {
    s/^/<rootroot>/ if $.==1;
    if (/(<link rel[^<>\n]*?)(\/?)(>)/g) {
        my ($p1, $p2, $p3) = ($1, $2, $3);
        s/$p1$p2$p3/($p2 ? qq|$p1$p2$p3<span class="entry">| : qq|$p1\/$p3<span class="entry">|)/ge;
    };
    s/<\/>/<entry_end><\/entry_end>/;
    s/$/<\/rootroot>/ if eof;

}

How can we save all the lines of changes in-place?

Because I need to do a quick validation on the html file using XML::LibXML right after the in-place change of the html source..

Thanks in advance.

Upvotes: 3

Views: 335

Answers (1)

H&#229;kon H&#230;gland
H&#229;kon H&#230;gland

Reputation: 40718

You can try something like this:

my $filename = 'test.dat';
@ARGV = ($filename);
$^I = '';
while(<<>>) {
    binmode(ARGV, ':raw');
    # Do the substitiution on $_ here ...
    print;
}

I did not find out how to set binmode before the loop, since ARGV is only defined after the <> operator has been used.

  • The $^I and ARGVvariables are decribed in perlvar

  • See perlop for information about why you should use <<>> instead of <>.

Some notes:

  • The while(<>) { ... } According to perlop, the loop
while (<>) {  ...         # code for each line
}

is equivalent to the following Perl-like pseudo code:

unshift(@ARGV, '-') unless @ARGV;   
while ($ARGV = shift) { 
    open(ARGV, $ARGV);
    while (<ARGV>) {    
       ...        # code for each line    
    }  
}  
  • Using in-place edit without a backup file: $^I="":

According to perlrun:

If no extension is supplied, and your system supports it, the original file is kept open without a name while the output is redirected to a new file with the original filename. When perl exits, cleanly or not, the original file is unlinked.

and some more information in this blog:

Perl opens and immediately unlink()s the original file, then opens a new file with the same name (new file descriptor and inode), and sends output to this second file; at the end, the old file is closed and thus deleted because it was unlinked, and what's left is a changed file with the same name as the original.

See also doio.c for actual implementation.

  • According to the above, the following might work:

    my $fn = 'test.dat';
    open ( my $fh, '<:raw', $fn ) or die "Could not open file '$fn': $!";
    unlink $fn or die "$!";
    open ( my $fh2, '>:raw', $fn ) or die "Could not reopen file '$fn': $!";
    while(<$fh>) {
        # Do the substitutions on $_ here ...
        print $fh2 $_;
    }
    close $fh;
    close $fh2;
    

Upvotes: 2

Related Questions