2012-12-19 11 views
9

Voglio scrivere qualcosa su un file il cui nome è nella variabile $filename. Non voglio sovrascrivere, quindi posso controllare prima se esiste e quindi aprirlo:Atomico aperto di file non esistente in Perl

#stage1 
if(-e $filename) 
{ 
    print "file $filename exists, not overwriting\n"; 
    exit 1; 
} 

#stage2 
open(OUTFILE, ">", $filename) or die $!; 

Ma questo non è atomica. Teoricamente qualcuno può creare questo file tra stage1 e stage2. Esiste qualche variante del comando open che farà entrambe queste cose in modo atomico, quindi non riuscirà ad aprire un file per scrivere se il file esiste?

risposta

4

Se siete preoccupati per più script Perl che modificano lo stesso file, basta usare la funzione flock() in ognuno di bloccare il file che ti interessa.

Se siete preoccupati per i processi esterni, che probabilmente non hai il controllo, puoi usare la funzione sysopen(). Secondo il di programmazione Perl libro (che consiglio vivamente, tra l'altro):

per risolvere questo problema di sovrascrittura, è necessario utilizzare sysopen, che fornisce singoli controlli sulla possibilità di creare un nuovo file o clobber uno esistente. E ci sbarazzeremo del test di esistenza del file –e dal momento che non serve a nessuno scopo utile qui e aumenta solo la nostra esposizione alle condizioni di gara.

Essi inoltre forniscono questo blocco di codice di esempio:

use Fcntl qw/O_WRONLY O_CREAT O_EXCL/; 
open(FH, "<", $file) 
    || sysopen(FH, $file, O_WRONLY | O_CREAT | O_EXCL) 
    || die "can't create new file $file: $!"; 

In questo esempio, prima tirare in alcune costanti (da utilizzare nella chiamata sysopen). Successivamente, tentano di aprire il file con open e, in caso di esito negativo, tentano con lo sysopen. Continuano a dire:

Ora, anche se il file di molle in qualche modo in esistenza tra quando è aperto fallisce e quando sysopen tenta di aprire un nuovo file per la scrittura, nessun danno è fatto, perché con le bandiere fornite, sysopen si rifiuterà di aprire un file già esistente.

Così, per rendere le cose chiare per la vostra situazione, rimuovere il file di test completamente (non più fase 1), e solo fare l'operazione di apertura utilizzando codice simile al blocco di cui sopra. Problema risolto!

+0

Questo ancora non risolve il * In teoria qualcuno può creare questo file tra stage1 e stage2 * problema! È solo un modo non invasivo di aprire i file. – creaktive

+0

Certo che lo fa. Ci siamo completamente sbarazzati della "fase 1" dalla domanda dell'OP. Ora c'è solo una fase: l'apertura del file! –

+0

Ci scusiamo per non essere d'accordo ... per quanto riguarda 'open (FH," <", $ file) || (! sonno 1) || sysopen (...) '? Ci sono ancora due fasi, basta metterle nella stessa frase.Per favore correggimi se sbaglio. – creaktive

6

Ecco un modo atomico di file di apertura: sessione

#!/usr/bin/env perl 
use strict; 
use warnings qw(all); 

use Fcntl qw(:DEFAULT :flock); 

my $filename = 'test'; 
my $fh; 

# this is "atomic open" part 
unless (sysopen($fh, $filename, O_CREAT | O_EXCL | O_WRONLY)) { 
    print "file $filename exists, not overwriting\n"; 
    exit 1; 
} 

# flock() isn't required for "atomic open" per se 
# but useful in real world usage like log appending 
flock($fh, LOCK_EX); 

# use the handle as you wish 
print $fh scalar localtime; 
print $fh "\n"; 

# unlock & close 
flock($fh, LOCK_UN); 
close $fh; 

Debug:

[email protected]:~/stackoverflow$ cat test 
Wed Dec 19 12:10:37 2012 
[email protected]:~/stackoverflow$ perl sysopen.pl 
file test exists, not overwriting 
[email protected]:~/stackoverflow$ cat test 
Wed Dec 19 12:10:37 2012 
+1

Se il gruppo è necessario, questo non risponde alla domanda dell'OP. È necessario, o il sysopen fa il trucco? – ikegami

+0

Grazie @ikegami, ho modificato la risposta per indicare che 'flock()' non è correlato alla domanda, anche se solitamente è presente nel codice simile. – creaktive