2015-05-29 11 views
6

Nella descrizione della funzione Perl -i[extension] a http://perldoc.perl.org/perlrun.html, codice che è sostanzialmente identico al seguente programma è data come "equivalente" per usando perl -pi.orig ...:Come è implementato perl -i * realmente *?

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

Questo funziona bene quando $extension eq '.orig'; tuttavia, Perl definisce -i senza alcuna estensione (ovvero, per $extension eq ''). comportamento definito di Perl è quello di modificare il file in luogo, senza file di backup creato:

Se nessuna estensione è fornito, e il vostro sistema supporta, il file originale viene mantenuto aperto senza un nome, mentre l'output viene reindirizzato a un nuovo file con il nome file originale. Quando perl esce, pulito o no, il file originale è scollegato.

Forse il mio sistema (Mac OS X Yosemite 10.10.3) non lo supporta.

Se ho impostato $extension = '' in questo programma, quindi il codice funziona bene per i file più piccolo di un blocco di STDIN (4096 byte in AcivePerl 5.10, ma 8192 byte di ActivePerl 5.16), ma sarà non lavoro per i file più grande di un blocco.

Sembra a me che, sul mio sistema, se $ARGV e $backup hanno lo stesso valore (cui si intende se $extension eq '', poi la chiamata open(ARGVOUT, ">$ARGV") on line 17 clobbers il file di input dopo un isolato di esso è stato letto.

Posso ovviare a questo, ovviamente, scrivendo su un file temporaneo e poi rinominandolo alla fine, ma sono un po 'deluso, dopo un paio di ore di debug, che l'esempio in perlrun non è così come mi ero aspettato.

  1. C'è un idioma standard modo migliore per gestire il caso $extension eq ''?

  2. È questo il caso d'uso $extension eq '' abbastanza importante che perlrun deve essere modificato? Naturalmente, la clausola "e il tuo sistema lo supporta" significa che l'esempio non è errato, ma l'esempio sarebbe più utile se riguardasse anche questo caso.

+2

Il motivo per il programma di esempio è corretta è che il programma di esempio è di circa '-i. orig', non su '-i' senza estensione. Non so perché ti aspetti che il programma di esempio si comporti come '-i' se cancelli semplicemente' .orig' all'interno del programma. (È come vedere '5 * 5 == 25' e assumendo che quindi' 8 * 8 == 28'.) La sua correttezza non ha nulla a che fare con la clausola "e il tuo sistema lo supporta", che riguarda se il tuo sistema supporta mantenendo un handle aperto per un file non collegato. – ruakh

+1

(A proposito, non è '-i ''', ma si trova semplicemente '-i' senza argomenti e il confronto tra stringhe usa' eq', non '=='.) – ruakh

+0

Anche se si potrebbe scrivere '-i '' '. – ikegami

risposta

11

Quando viene fornita estensione:

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

Questo può essere visto utilizzando 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 
... 

Quando è prevista alcuna estensione:

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

questo può essere visto utilizzando strace.

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

sistemi Unix come Mac supportano i file anonimi.Windows no, quindi -i richiede un'estensione lì.

>perl -i.bak -pe1 a 

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

Se integriamo questa conoscenza nel codice che avete inviato, otteniamo la seguente:

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

e cygwin imposta solo in modo silenzioso bare -i per significare -i.bak (presumibilmente per un migliore supporto degli script di shell esistenti) – ysth

+1

@ysth, Quindi la cosa strana è che il trucco di collegamento funziona in cygwin. (Non so come.) Forse non funzionava? O forse ci sono dei limiti? – ikegami

+1

è passato molto tempo, quindi probabilmente mi ricordo male, ma era qualcosa come impostare un'eliminazione sul flag di chiusura tramite l'API di win32 o effettivamente eliminarlo in un gestore di uscita o entrambi? e forse con limitazioni (un altro processo che lo ha aperto troppo?) – ysth

Problemi correlati