open - perl read file line by line
¿Cómo bloqueo un archivo en Perl? (13)
¿Has considerado utilizar el módulo LockFile :: Simple ? Ya hace la mayor parte del trabajo por ti.
En mi experiencia pasada, he encontrado que es muy fácil de usar y robusto.
¿Cuál es la mejor manera de crear un bloqueo en un archivo en Perl?
¿Es mejor agruparse en el archivo o crear un archivo de bloqueo para colocar un candado y comprobar si hay un candado en el archivo de bloqueo?
Aquí está mi solución para leer y escribir en una cerradura ...
open (TST,"+< readwrite_test.txt") or die "Cannot open file/n$!";
flock(TST, LOCK_EX);
# Read the file:
@LINES=<TST>;
# Wipe the file:
seek(TST, 0, 0); truncate(TST, 0);
# Do something with the contents here:
push @LINES,"grappig, he!/n";
$LINES[3]="Gekke henkie!/n";
# Write the file:
foreach $l (@LINES)
{
print TST $l;
}
close(TST) or die "Cannot close file/n$!";
CPAN al rescate: IO::LockedFile .
Creo que sería mucho mejor mostrar esto con variables léxicas como manejadores de archivos y manejo de errores. También es mejor usar las constantes del módulo Fcntl que codificar el número mágico 2, que podría no ser el número correcto en todos los sistemas operativos.
use Fcntl '':flock''; # import LOCK_* constants # open the file for appending open (my $fh, ''>>'', ''test.dat'') or die $!; # try to lock the file exclusively, will wait till you get the lock flock($fh, LOCK_EX); # do something with the file here (print to it in our case) # actually you should not unlock the file # close the file will unlock it close($fh) or warn "Could not close file $!";
Consulte la documentación completa de la bandada y el tutorial de bloqueo de archivos en PerlMonks, aunque también use el estilo anterior de uso del manejador de archivos.
En realidad, generalmente omito el manejo de errores en close () ya que no hay mucho que pueda hacer si falla de todos modos.
En cuanto a qué bloquear, si está trabajando en un solo archivo, bloquee ese archivo. Si necesita bloquear varios archivos a la vez, entonces - para evitar bloqueos muertos - es mejor elegir un archivo que está bloqueando. Realmente no importa si ese es uno de los varios archivos que realmente necesita bloquear o un archivo separado que crea solo para el propósito de bloqueo.
Desarrollado fuera de http://metacpan.org/pod/File::FcntlLock
use Fcntl qw(:DEFAULT :flock :seek :Fcompat);
use File::FcntlLock;
sub acquire_lock {
my $fn = shift;
my $justPrint = shift || 0;
confess "Too many args" if defined shift;
confess "Not enough args" if !defined $justPrint;
my $rv = TRUE;
my $fh;
sysopen($fh, $fn, O_RDWR | O_CREAT) or LOGDIE "failed to open: $fn: $!";
$fh->autoflush(1);
ALWAYS "acquiring lock: $fn";
my $fs = new File::FcntlLock;
$fs->l_type( F_WRLCK );
$fs->l_whence( SEEK_SET );
$fs->l_start( 0 );
$fs->lock( $fh, F_SETLKW ) or LOGDIE "failed to get write lock: $fn:" . $fs->error;
my $num = <$fh> || 0;
return ($fh, $num);
}
sub release_lock {
my $fn = shift;
my $fh = shift;
my $num = shift;
my $justPrint = shift || 0;
seek($fh, 0, SEEK_SET) or LOGDIE "seek failed: $fn: $!";
print $fh "$num/n" or LOGDIE "write failed: $fn: $!";
truncate($fh, tell($fh)) or LOGDIE "truncate failed: $fn: $!";
my $fs = new File::FcntlLock;
$fs->l_type(F_UNLCK);
ALWAYS "releasing lock: $fn";
$fs->lock( $fh, F_SETLK ) or LOGDIE "unlock failed: $fn: " . $fs->error;
close($fh) or LOGDIE "close failed: $fn: $!";
}
Las otras respuestas cubren el bloqueo de perl de Perl bastante bien, pero en muchos sistemas Unix / Linux, en realidad existen dos sistemas de bloqueo independientes: BSD flock () y POSIX fcntl () basados en bloqueos.
A menos que proporcione opciones especiales para configurar al construir Perl, su bandada utilizará flock () si está disponible. En general, esto es bueno y probablemente lo que desee si solo necesita bloquear dentro de su aplicación (se ejecuta en un solo sistema). Sin embargo, algunas veces necesita interactuar con otra aplicación que use bloqueos fcntl () (como Sendmail, en muchos sistemas) o quizás necesite bloquear archivos en sistemas de archivos montados en NFS.
En esos casos, es posible que desee ver File::FcntlLock o File::lockf . También es posible hacer un bloqueo basado en fcntl () en Perl puro (con algunos bits peludos y no portátiles del paquete ()).
Descripción rápida de las diferencias flock / fcntl / lockf:
lockf casi siempre se implementa en la parte superior de fcntl, solo tiene bloqueo de nivel de archivo. Si se implementa utilizando fcntl, las limitaciones a continuación también se aplican a lockf.
fcntl proporciona el bloqueo de nivel de rango (dentro de un archivo) y el bloqueo de red a través de NFS, pero los bloqueos no son heredados por procesos secundarios después de un fork (). En muchos sistemas, debe tener el manejador de archivos abierto de solo lectura para solicitar un bloqueo compartido y leer y escribir para solicitar un bloqueo exclusivo.
flock solo tiene bloqueo de nivel de archivo, el bloqueo solo se realiza en una sola máquina (puede bloquear un archivo montado en NFS, pero solo los procesos locales verán el bloqueo). Los bloqueos son heredados por los niños (suponiendo que el descriptor de archivo no está cerrado).
En ocasiones, el rebaño (sistemas SYSV) se emula utilizando lockf o fcntl; en algunos sistemas BSD lockf se emula utilizando flock. En general, este tipo de emulación funciona mal y se recomienda evitarlas.
Mi objetivo en esta pregunta era bloquear un archivo que se utiliza como un almacén de datos para varios scripts. Al final, utilicé un código similar al siguiente (de Chris):
open (FILE, ''>>'', test.dat'') ; # open the file
flock FILE, 2; # try to lock the file
# do something with the file here
close(FILE); # close the file
En su ejemplo, eliminé el flock FILE, 8 ya que el close (FILE) también realiza esta acción. El verdadero problema fue cuando el script comienza tiene que contener el contador actual, y cuando termina tiene que actualizar el contador. Aquí es donde Perl tiene un problema, para leer el archivo usted:
open (FILE, ''<'', test.dat'');
flock FILE, 2;
Ahora quiero escribir los resultados y, dado que quiero sobrescribir el archivo, necesito volver a abrir y truncar, lo que da como resultado lo siguiente:
open (FILE, ''>'', test.dat''); #single arrow truncates double appends
flock FILE, 2;
En este caso, el archivo está realmente desbloqueado durante un corto período de tiempo mientras se vuelve a abrir el archivo. Esto demuestra el caso del archivo de bloqueo externo. Si va a cambiar los contextos del archivo, use un archivo de bloqueo. El código modificado:
open (LOCK_FILE, ''<'', test.dat.lock'') or die "Could not obtain lock";
flock LOCK_FILE, 2;
open (FILE, ''<'', test.dat'') or die "Could not open file";
# read file
# ...
open (FILE, ''>'', test.dat'') or die "Could not reopen file";
#write file
close (FILE);
close (LOCK_FILE);
Ryan P escribió:
En este caso, el archivo está realmente desbloqueado durante un corto período de tiempo mientras se vuelve a abrir el archivo.
Entonces no hagas eso. En su lugar, open
el archivo para lectura / escritura:
open my $fh, ''+<'', ''test.dat''
or die "Couldn’t open test.dat: $!/n";
Cuando esté listo para escribir el contador, simplemente vuelva al inicio del archivo. Tenga en cuenta que si hace eso, debe truncate
justo antes de close
, de modo que el archivo no quede con basura arrastrada si sus nuevos contenidos son más cortos que los anteriores. (Por lo general, la posición actual en el archivo está al final, por lo que puede escribir truncate $fh, tell $fh
).
Además, tenga en cuenta que utilicé el manejador de archivos open
y el archivo léxico, y también verifiqué el éxito de la operación. Evite los manejadores de archivos globales (¿las variables globales son malas, mmkay?) Y la open
mágica de dos argumentos (que ha sido fuente de muchos errores de n (explotables) en el código de Perl), y siempre pruebe si su open
es exitosa.
Si terminas usando rebaño, aquí tienes un código para hacerlo:
use Fcntl '':flock''; # Import LOCK_* constants
# We will use this file path in error messages and function calls.
# Don''t type it out more than once in your code. Use a variable.
my $file = ''/path/to/some/file'';
# Open the file for appending. Note the file path is quoted
# in the error message. This helps debug situations where you
# have a stray space at the start or end of the path.
open(my $fh, ''>>'', $file) or die "Could not open ''$file'' - $!";
# Get exclusive lock (will block until it does)
flock($fh, LOCK_EX) or die "Could not lock ''$file'' - $!";
# Do something with the file here...
# Do NOT use flock() to unlock the file if you wrote to the
# file in the "do something" section above. This could create
# a race condition. The close() call below will unlock the
# file for you, but only after writing any buffered data.
# In a world of buffered i/o, some or all of your data may not
# be written until close() completes. Always, always, ALWAYS
# check the return value of close() if you wrote to the file!
close($fh) or die "Could not write ''$file'' - $!";
Algunos enlaces útiles:
En respuesta a su pregunta agregada, diría que coloque el candado en el archivo o cree un archivo que llame ''bloquear'' siempre que el archivo esté bloqueado y elimínelo cuando ya no esté bloqueado (y luego asegúrese de que sus programas obedezcan esa semántica).
Una alternativa al enfoque de archivo de bloqueo es usar un socket de bloqueo. Ver Lock::Socket en CPAN para tal implementación. El uso es tan simple como lo siguiente:
use Lock::Socket qw/lock_socket/;
my $lock = lock_socket(5197); # raises exception if lock already taken
Hay un par de ventajas al usar un socket:
- garantizado (a través del sistema operativo) que no hay dos aplicaciones que mantengan el mismo bloqueo: no hay condición de carrera.
- garantizado (de nuevo a través del sistema operativo) para limpiar claramente cuando su proceso finaliza, por lo que no hay bloqueos añejos para hacer frente.
- se basa en una funcionalidad que es bien soportada por cualquier cosa en la que Perl se ejecute: sin problemas con el soporte de flock (2) en Win32, por ejemplo.
La desventaja obvia es, por supuesto, que el espacio de nombres de bloqueo es global. Es posible un tipo de denegación de servicio si otro proceso decide bloquear el puerto que necesita.
[revelación: soy el autor del módulo mencionado anteriormente]
flock crea bloqueos de archivos de estilo Unix, y está disponible en la mayoría de las ejecuciones de Perl del sistema operativo. Sin embargo, los bloqueos de bandadas son solo de asesoramiento.
editar: enfatizaron que el rebaño es portátil
use strict;
use Fcntl '':flock''; # Import LOCK_* constants
# We will use this file path in error messages and function calls.
# Don''t type it out more than once in your code. Use a variable.
my $file = ''/path/to/some/file'';
# Open the file for appending. Note the file path is in quoted
# in the error message. This helps debug situations where you
# have a stray space at the start or end of the path.
open(my $fh, ''>>'', $file) or die "Could not open ''$file'' - $!";
# Get exclusive lock (will block until it does)
flock($fh, LOCK_EX);
# Do something with the file here...
# Do NOT use flock() to unlock the file if you wrote to the
# file in the "do something" section above. This could create
# a race condition. The close() call below will unlock it
# for you, but only after writing any buffered data.
# In a world of buffered i/o, some or all of your data will not
# be written until close() completes. Always, always, ALWAYS
# check the return value on close()!
close($fh) or die "Could not write ''$file'' - $!";