2011-05-19 8 views
14

Tengo un programa que tiene una cantidad de nombres de archivos configurados internamente. El programa edita una serie de archivos de configuración asociados con una cuenta de base de datos, luego cambia la contraseña de la base de datos para la cuenta de la base de datos. La lista de archivos de configuración está asociada con el nombre de la cuenta de la base de datos a través de una lista interna. Cuando procesar estos archivos, tengo el siguiente bucle en mi programa:Necesita la edición perl in place de los archivos que no están en la línea de comandos

BEGIN { $^I = '.oldPW'; } # Enable in-place editing 
... 
foreach (@{$Services{$request}{'files'}}) 
{ 
    my $filename = $Services{$request}{'configDir'} . '/' . $_; 
    print "Processing ${filename}\n"; 
    open CONFIGFILE, '+<', $filename or warn $!; 
    while (<CONFIGFILE>) 
    { 
     s/$oldPass/$newPass/; 
     print; 
    } 
    close CONFIGFILE; 
} 

El problema es que esto escribe la salida modificada en STDOUT, no CONFIGFILE. ¿Cómo puedo editar esto en realidad? Mueve $^I dentro del ciclo? imprimir CONFIGFILE? Estoy perplejo.

Actualización: encontré lo que estaba buscando en perlmonks. Puede usar un ARGV local dentro del ciclo para hacer la edición in situ de la manera normal. El bucle de arriba ahora se ve así:

foreach (@{$Services{$request}{'files'}}) 
{ 
    my $filename = $Services{$request}{'configDir'} . '/' . $_; 
    print "Processing ${filename}\n"; 
    { 
     local @ARGV = ($filename); 
     while (<>) 
     { 
      s/$oldPass/$newPass/; 
      print; 
     } 
    } 
} 

Si no fuera por que añade el ConfigDir en principio, tan sólo pudiera cara o cruz toda la lista en el @ARGV local, pero esto es lo suficientemente eficiente.

Gracias por las útiles sugerencias sobre Tie :: File, probablemente iría por ese camino si se hacía de esta manera. Los archivos de configuración que estoy editando no tienen más de unos pocos Kbytes de longitud, por lo que un Lazo no usaría demasiada memoria.

Respuesta

10

La variable $^I solo funciona en la secuencia de nombres de archivo que se encuentra en $ARGV utilizando la construcción <> vacía. Tal vez algo como esto funcionaría:

BEGIN { $^I = '.oldPW'; } # Enable in-place editing 
... 

local @ARGV = map { 
    $Services{$request}{'configDir'} . '/' . $_ 
} @{$Services{$request}{'files'}}; 
while (<>) { 
    s/$oldPass/$newPass/; 

    # print? print ARGVOUT? I don't remember 
    print ARGVOUT; 
} 

pero si no es un simple script y que necesita @ARGV y STDOUT para otros fines, usted es probablemente mejor usar algo como Tie::File para esta tarea:

use Tie::File; 
foreach (@{$Services{$request}{'files'}}) 
{ 
    my $filename = $Services{$request}{'configDir'} . '/' . $_; 

    # make the backup yourself 
    system("cp $filename $filename.oldPW"); # also consider File::Copy 

    my @array; 
    tie @array, 'Tie::File', $filename; 

    # now edit @array 
    s/$oldPass/$newPass/ for @array; 

    # untie to trigger rewriting the file 
    untie @array; 
} 
+0

En realidad, no necesito copias de seguridad porque estos archivos están almacenados en un sistema de control de código fuente. El siguiente paso en este script es integrar el SCCS (Perforce, en este caso) en el script, de modo que se compruebe y envíe los archivos a medida que se actualicen. – Wexxor

15

Las versiones recientes de File::Slurp proporcionan funciones convenientes, edit_file y edit_file_lines. La parte interna de su código sería:

use File::Slurp qw(edit_file); 
edit_file { s/$oldPass/$newPass/g } $filename; 
+2

No olvide hacer una copia de seguridad de su archivo. – mob

2

Tie :: El archivo ya se ha mencionado, y es muy simple. Evitar el modificador -i es probablemente una buena idea para los scripts que no son de línea de comandos. Si usted está buscando para evitar Tie :: File, la solución estándar es la siguiente:

  • Abrir un archivo para la entrada
  • Abrir un archivo temporal para la salida
  • leer una línea del archivo de entrada.
  • Modifique la línea de la manera que desee.
  • Escriba la nueva línea en su archivo temporal.
  • Pasa a la siguiente línea, etc.
  • Cierre los archivos de entrada y salida.
  • Cambie el nombre del archivo de entrada a algún nombre de copia de seguridad, como la adición de .bak al nombre del archivo.
  • Cambie el nombre del archivo de salida temporal al nombre de archivo de entrada original.

Esto es básicamente lo que ocurre entre bastidores con el conmutador -i.bak de todos modos, pero con mayor flexibilidad.

Cuestiones relacionadas