How does perl -i * actually work?

In the description of the Perl -i[extension] function at http://perldoc.perl.org/perlrun.html, code that is substantially identical to the following program is indicated as the "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 without an extension (i.e. for $extension eq '' ). Perl defined behavior to edit the file in place, without creating a backup file:

If the extension is not provided and your system supports it, the original file remains open without a name, while the output is redirected to a new file with the original file name. When perl exits, cleanly or not, the source file is detached.

Maybe my system (Mac OS X Yosemite 10.10.3) does not support it.

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

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

I can get around this, of course, by writing to a temporary file and then renaming it at the end. But I'm a little disappointed, after a couple of hours of debugging, that the perlrun example is not as general as I expected.

  • Is there a standard, idiomatic way to solve the problem of $extension eq '' ?

  • Is this $extension eq '' use case important enough for Perlrun to be edited? Of course, the sentence "and your system supports this" means that the example is not incorrect, but the example would be more useful if it also covered this case.

+6
source share
1 answer

If an extension is provided:

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

This can be seen with 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 ... 

If the extension is not provided:

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

This can be seen with 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 like Macs support anonymous files. Windows does not work, so -i requires an extension.

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

If we integrate this knowledge into the code that you published, 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); 
+11
source

Source: https://habr.com/ru/post/988203/


All Articles