Ha habido varias preguntas aquí con respecto a la sustitución de cadenas de varias líneas con el shell de Unix, pero no he encontrado ninguna que funcione en esta situación.
Estoy tratando de eliminar claves y restricciones de algunos MySQL DDL, que se ve así (un ejemplo):
CREATE TABLE `access_group` (
`GROUP_ID` int(10) NOT NULL AUTO_INCREMENT,
`PARENT_GROUP_ID` int(10) DEFAULT NULL,
`GROUP_NAME` varchar(45) NOT NULL,
`GROUP_DESC` varchar(45) NOT NULL DEFAULT '',
PRIMARY KEY (`GROUP_ID`),
KEY `testkey` (`PARENT_GROUP_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=66 DEFAULT CHARSET=latin1;
Quiero eliminar todo, desde la coma que termina la línea antes de 'CLAVE PRIMARIA' hasta, pero sin incluir ') MOTOR =' (puede haber cero o varias líneas entre estas, y no siempre comenzarán con KEY o tendrán el paréntesis, pero el ') ENGINE=' es consistente). El resultado debería verse así:
CREATE TABLE `access_group` (
`GROUP_ID` int(10) NOT NULL AUTO_INCREMENT,
`PARENT_GROUP_ID` int(10) DEFAULT NULL,
`GROUP_NAME` varchar(45) NOT NULL,
`GROUP_DESC` varchar(45) NOT NULL DEFAULT ''
) ENGINE=InnoDB AUTO_INCREMENT=66 DEFAULT CHARSET=latin1;
Estoy abierto a usar cualquier utilidad de línea de comandos estándar (por ejemplo, sed, perl, awk), pero dado que estos archivos pueden ser bastante grandes (algunos son del orden de decenas o cientos de GB), deben ser eficientes. Dado que los archivos generalmente se almacenan comprimidos con gzip (o, a veces, proceso la salida de la utilidad de volcado mysql directamente en lugar de escribir primero en el disco), necesito algo que se pueda canalizar hacia adentro y hacia afuera.
Respuesta aceptada:
Mantenga el estado sobre si imprimir la línea anterior, edite dicho para eliminar la coma cuando sea necesario. Este método solo mantiene una o dos líneas del archivo en la memoria.
#!/usr/bin/env perl
use strict;
use warnings;
my $printing = 1;
my $previous;
# reads from standard input (optionally with the conventional -) or from
# the named files
shift @ARGV if @ARGV == 1 and $ARGV[0] eq '-';
while ( my $line = readline ) {
if ( $line =~ m/^\s+PRIMARY KEY/ ) {
$previous =~ s/,[ \t]*$//;
$printing = 0;
} elsif ( $line =~ m/^\) ENGINE/ ) {
$printing = 1;
} elsif ( !$printing ) {
undef $previous;
}
print $previous if defined $previous;
$previous = $line if $printing;
}
# don't forget last line after fall off the end of input (eof)
print $previous if defined $previous;