2015-05-29 4 views
6

In der Beschreibung der Funktion Perl -i[extension] bei http://perldoc.perl.org/perlrun.html, Code, der als „äquivalent“ zu verwenden perl -pi.orig ... gegeben ist wesentlich identisch mit dem folgenden Programm:Wie ist Perl * wirklich * implementiert?

#!/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); 

Dies funktioniert gut, wenn $extension eq '.orig'; Perl definiert jedoch -i ohne Erweiterung (also für $extension eq ''). Perl definiertes Verhalten ist die Datei an seinem Platz zu bearbeiten, ohne Backup-Datei erstellt:

Wenn keine Erweiterung geliefert wird, und das System unterstützt, die ursprüngliche Datei ohne Namen offen gehalten wird, während der Ausgang umgeleitet wird zu einer neue Datei mit dem ursprünglichen Dateinamen. Wenn Perl sauber oder nicht existiert, wird die ursprüngliche Datei nicht verknüpft.

Vielleicht unterstützt mein System (Mac OS X Yosemite 10.10.3) es nicht.

Wenn ich $extension = '' in diesem Programm gesetzt, dann wird der Code für die Dateien, die kleiner als einen Block von STDIN (4096 Bytes in AcivePerl 5.10, aber 8192 Bytes von ActivePerl 5.16) gut funktionieren, aber es wird nicht Arbeit für Dateien größer als ein Block.

Es sieht für mich, dass auf meinem System, wenn $ARGV und $backup den gleichen Wert haben (was sie, wenn $extension eq '', dann ist der open(ARGVOUT, ">$ARGV") Anruf auf der Leitung 17 clobbers die Eingabedatei nach einem Block von ihm gelesen wurde.

Ich kann das natürlich umgehen, indem ich in eine temporäre Datei schreibe und sie am Ende umbenenne.Aber ich bin ein wenig enttäuscht, nach ein paar Stunden Debugging, dass das Beispiel in Perlrun nicht so ist Allzweck, wie ich es erwartet hatte

  1. Gibt es einen Standard, idiomat ic Weg, um mit dem $extension eq '' Fall umzugehen?

  2. Ist dieser $extension eq '' Anwendungsfall wichtig genug, dass perlrun bearbeitet werden sollte? Natürlich bedeutet die Klausel "und Ihr System unterstützt es", dass das Beispiel nicht falsch ist, aber das Beispiel wäre nützlicher, wenn es auch diesen Fall abdeckt.

+2

Der Grund für die Beispiel-Programm korrekt ist, ist, dass das Beispielprogramm über '-i ist. orig', nicht über '-i' ohne Erweiterung. Ich weiß nicht, warum Sie erwarten, dass sich das Beispielprogramm wie "-i" verhält, wenn Sie lediglich die '.orig' im Programm löschen. (Das ist so, als ob man '5 * 5 == 25 'sieht und davon ausgeht, dass' 8 * 8 == 28 'ist.) Seine Korrektheit hat nichts mit der Klausel" und Ihr System unterstützt es "zu tun, ob Ihr System dies unterstützt Halten Sie einen offenen Handle zu einer nicht verknüpften Datei. – ruakh

+1

(Übrigens ist es nicht '-i ''', sondern einfach nur '-i' ohne Argument. Und String-Vergleich benutzt' eq', nicht '=='.) – ruakh

+0

Obwohl Sie es '-i schreiben könnten "". – ikegami

Antwort

11

Wenn Erweiterung vorgesehen ist:

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

Dies kann mit strace zu sehen.

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

Wenn keine Erweiterung vorgesehen ist:

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

Dies kann mit strace zu sehen.

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

Unix-Systeme wie Macs unterstützen anonyme Dateien.Windows tut das nicht, also benötigt -i dort eine Erweiterung.

>perl -i.bak -pe1 a 

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

Wenn wir dieses Wissen in den Code integrieren Sie auf dem Laufenden, erhalten wir folgendes:

#!/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); 
+1

und cygwin nur ruhig standardmäßig bare -i zu bedeuten -i.bak (vermutlich für eine bessere Unterstützung der bestehenden Shell-Skripte) – ysth

+1

@ysth, Dann ist seltsame Sache, dass der Unlink-Trick in Cygwin funktioniert. (Keine Ahnung wie.) Vielleicht hat es nicht funktioniert? Oder vielleicht gibt es einige Einschränkungen? – ikegami

+1

es ist eine lange Zeit, so dass ich mich wahrscheinlich falsch erinnere, aber es war so etwas wie ein Löschen auf Close-Flag über die Win32 API oder tatsächlich Löschen in einem Exit-Handler oder beides? und vielleicht mit Einschränkungen (ein anderer Prozess hat es auch geöffnet?) – ysth