texto predictivo autocorrector perl search command-line replace bulk

perl - predictivo - autocorrector iphone



¿Hay una manera simple de hacer la sustitución de texto a granel en su lugar? (6)

Puede estar interesado en File :: Transaction :: Atomic o File :: Transaction

El SINOPSIS de F :: T :: A es muy similar a lo que intenta hacer:

# In this example, we wish to replace # the word ''foo'' with the word ''bar'' in several files, # with no risk of ending up with the replacement done # in some files but not in others. use File::Transaction::Atomic; my $ft = File::Transaction::Atomic->new; eval { foreach my $file (@list_of_file_names) { $ft->linewise_rewrite($file, sub { s#/bfoo/b#bar#g; }); } }; if ($@) { $ft->revert; die "update aborted: $@"; } else { $ft->commit; }

Aúna eso con el Archivo :: Encuentra que ya has escrito, y deberías estar listo.

He intentado codificar una secuencia de comandos Perl para sustituir texto en todos los archivos fuente de mi proyecto. Necesito algo como:

perl -p -i.bak -e "s/thisgoesout/thisgoesin/gi" *.{cs,aspx,ascx}

Pero eso analiza todos los archivos de un directorio recursivamente .

Acabo de comenzar un script:

use File::Find::Rule; use strict; my @files = (File::Find::Rule->file()->name(''*.cs'',''*.aspx'',''*.ascx'')->in(''.'')); foreach my $f (@files){ if ($f =~ s/thisgoesout/thisgoesin/gi) { # In-place file editing, or something like that } }

Pero ahora estoy atascado. ¿Existe una manera simple de editar todos los archivos en su lugar usando Perl?

Tenga en cuenta que no necesito guardar una copia de cada archivo modificado; Estoy teniendo todos subversionados =)

Actualización : Probé esto en Cygwin ,

perl -p -i.bak -e "s/thisgoesout/thisgoesin/gi" {*,*/*,*/*/*}.{cs,aspx,ascx

Pero parece que mi lista de argumentos explotó al tamaño máximo permitido. De hecho, estoy obteniendo errores muy extraños en Cygwin ...


Puede usar Tie :: File para acceder de forma scalable a archivos grandes y cambiarlos en su lugar. Ver la página de manual (man 3perl Tie :: File).


Puedes usar find :

find . -name ''*.{cs,aspx,ascx}'' | xargs perl -p -i.bak -e "s/thisgoesout/thisgoesin/gi"

Esto listará todos los nombres de archivos recursivamente, luego xargs leerá su stdin y ejecutará el resto de la línea de comando con los nombres de archivo agregados al final. Una cosa buena de xargs es que ejecutará la línea de comando más de una vez si la línea de comando que construye se vuelve demasiado larga para ejecutarse de una vez.

Tenga en cuenta que no estoy seguro de si find entiende completamente todos los métodos de shell de selección de archivos, por lo que si lo anterior no funciona, quizás intente:

find . | grep -E ''(cs|aspx|ascx)$'' | xargs ...

Cuando uso tuberías como esta, me gusta construir la línea de comando y ejecutar cada parte individualmente antes de continuar, para asegurarme de que cada programa obtenga la entrada que desea. Entonces puedes ejecutar la pieza sin xargs primero para verificarlo.

Se me acaba de ocurrir que, aunque usted no lo haya dicho, probablemente esté en Windows debido a los sufijos de archivo que está buscando. En ese caso, la tubería anterior podría ejecutarse utilizando Cygwin. Es posible escribir una secuencia de comandos de Perl para hacer lo mismo, como comenzó a hacer, pero tendrá que hacer la edición in situ usted mismo porque no puede aprovechar el modificador -i en esa situación.


Si asigna @ARGV antes de usar *ARGV (también conocido como diamante <> ), $^I / -i funcionará en esos archivos en lugar de lo que se especificó en la línea de comando.

use File::Find::Rule; use strict; @ARGV = (File::Find::Rule->file()->name(''*.cs'', ''*.aspx'', ''*.ascx'')->in(''.'')); $^I = ''.bak''; # or set `-i` in the #! line or on the command-line while (<>) { s/thisgoesout/thisgoesin/gi; print; }

Esto debería hacer exactamente lo que quieras.

Si su patrón puede abarcar varias líneas, agregue un undef $/; antes del <> para que Perl opere en un archivo completo a la vez en lugar de línea por línea.


Cambio

foreach my $f (@files){ if ($f =~ s/thisgoesout/thisgoesin/gi) { #inplace file editing, or something like that } }

A

foreach my $f (@files){ open my $in, ''<'', $f; open my $out, ''>'', "$f.out"; while (my $line = <$in>){ chomp $line; $line =~ s/thisgoesout/thisgoesin/gi print $out "$line/n"; } }

Esto supone que el patrón no abarca múltiples líneas. Si el patrón puede abarcar líneas, tendrá que sorber el contenido del archivo. ("sorber" es un término Perl bastante común).

La chompa en realidad no es necesaria, me acaban de picar líneas que no fueron chomp demasiadas veces (si sueltas la chomp , cambia la print $out "$line/n"; para print $out $line; ).

Del mismo modo, puede cambiar open my $out, ''>'', "$f.out"; para open my $out, ''>'', undef; para abrir un archivo temporal y luego copiar ese archivo de nuevo sobre el original cuando se realiza la sustitución. De hecho, y especialmente si consume todo el archivo, simplemente puede hacer la sustitución en la memoria y luego escribir sobre el archivo original. Pero he cometido suficientes errores al hacer eso que siempre escribo en un nuevo archivo y verifico el contenido.

Tenga en cuenta que originalmente tenía una declaración if en ese código. Eso probablemente estuvo mal. Eso solo habría sido copiado a través de líneas que coincidían con la expresión regular "thisgoesout" (reemplazándola por "thisgoesin", por supuesto) mientras engullía silenciosamente el resto.


Gracias a ephemient en esta pregunta y en esta respuesta , obtuve esto:

use File::Find::Rule; use strict; sub ReplaceText { my $regex = shift; my $replace = shift; @ARGV = (File::Find::Rule->file()->name(''*.cs'',''*.aspx'',''*.ascx'')->in(''.'')); $^I = ''.bak''; while (<>) { s/$regex/$replace->()/gie; print; } } ReplaceText qr/some(crazy)regexp/, sub { "some $1 text" };

¡Ahora puedo incluso recorrer un hash que contenga regexp => entradas subs!