Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is perl -i *really* implemented?

Tags:

stdin

perl

In the description of the Perl -i[extension] feature at http://perldoc.perl.org/perlrun.html, code that is materially identical to the following program is given as "an equivalent" to using perl -pi.orig ...:

#!/usr/bin/perl

use strict;
use warnings;

my $extension = '.orig';
my $oldargv = '';
my $backup;
LINE: while (<>) {
    if ($ARGV ne $oldargv) {
        if ($extension !~ /\*/) {
            $backup = $ARGV . $extension;
        } else {
            ($backup = $extension) =~ s/\*/$ARGV/g;
        }
        rename($ARGV, $backup);
        open(ARGVOUT, ">$ARGV");
        select(ARGVOUT);
        $oldargv = $ARGV;
    }
    # Don't change anything; just copy.
}
continue {
    print;
}
select(STDOUT);

This works fine when $extension eq '.orig'; however, Perl defines -i with no extension as well (that is, for $extension eq ''). Perl's defined behavior is to edit the file in place, with no backup file created:

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.

Perhaps my system (Mac OS X Yosemite 10.10.3) doesn't support it.

If I set $extension = '' in this program, then the code will work fine for files smaller than one block of STDIN (4096 bytes in AcivePerl 5.10, but 8192 bytes by ActivePerl 5.16), but it will not work for files larger than one block.

It looks to me that, on my system, if $ARGV and $backup have the same value (which they will if $extension eq '', then the open(ARGVOUT, ">$ARGV") call on line 17 clobbers the input file after one block of it has been read.

I can work around this, of course, by writing to a temporary file and then renaming it at the end. But I'm a bit disappointed, after a couple hours of debugging, that the example in perlrun isn't as general-purpose as I had expected.

  1. Is there a standard, idiomatic way to deal with the $extension eq '' case?

  2. Is this $extension eq '' use-case important enough that perlrun should be edited? Of course, the "and your system supports it" clause means that the example is not incorrect, but the example would be more useful if it covered this case, too.

like image 636
Cary Millsap Avatar asked May 29 '15 20:05

Cary Millsap


People also ask

How is Perl so fast?

Some things perl does under the hood to be fast are that integers are (mostly) actually integers under the hood. Arrays are actually arrays under the hood (a fact that makes perl's DBI very fast).

How Perl is executed?

Perl is a fairly straightforward, widely known and well-respected scripting language. It is used for a variety of tasks (for example, you can use it to create the equivalent of DOS batch files or C shell scripts), but in the context of Web development it is used to develop CGI scripts.

Does Perl use compiler or interpreter?

Perl is an interpreted language, which means that your code can be run as-is, without a compilation stage that creates a non-portable executable program. Traditional compilers convert programs into machine language.

What does Perl stand for and how do we check for it?

Perl, which has sometimes been said to stand for “practical extraction and report language,” was influenced by existing programming languages—such as C, BASIC, and AWK—but it also reflected Wall's linguistic background in its extensive use of common English words.


1 Answers

Perl 5.28 changed -i. This answer pertains to earlier versions of Perl.


When an extension is provided:

open(my $fh_in,  '<', $qfn);
rename($qfn, "$qfn$ext");
open(my $fh_out, '>', $qfn);

This can be seen using strace.

$ strace perl -i~ -pe1 a
...
open("a", O_RDONLY)                     = 3
rename("a", "a~")                       = 0
open("a", O_WRONLY|O_CREAT|O_EXCL, 0600) = 4
...

When no extension is provided:

open(my $fh_in,  '<', $qfn);
unlink($qfn);
open(my $fh_out, '>', $qfn);

This can be seen using strace.

$ strace perl -i -pe1 a
...
open("a", O_RDONLY)                     = 3
unlink("a")                             = 0
open("a", O_WRONLY|O_CREAT|O_EXCL, 0600) = 4
...

Unix systems such as Macs support anonymous files. Windows does not, so -i requires an extension there.

>perl -i.bak -pe1 a

>perl -i -pe1 a
Can't do inplace edit without backup.

If we integrate this knowledge in the code you posted, we get the following:

#!/usr/bin/perl

use strict;
use warnings;

my $extension = '.orig';
my $oldargv = '';
my $backup;
LINE: while (<>) {
    if ($ARGV ne $oldargv) {
        if (length($extension)) {
            if ($extension !~ /\*/) {
                $backup = $ARGV . $extension;
            } else {
                ($backup = $extension) =~ s/\*/$ARGV/g;
            }
            rename($ARGV, $backup);
        } else {
            die("Can't do inplace edit without backup.\n") if $^O eq 'MSWin32';
            unlink($ARGV);
        }
        open(ARGVOUT, ">$ARGV");
        select(ARGVOUT);
        $oldargv = $ARGV;
    }
    # Don't change anything; just copy.
}
continue {
    print;
}
select(STDOUT);
like image 57
ikegami Avatar answered Nov 18 '22 09:11

ikegami