GNU/Linux >> Tutoriales Linux >  >> Linux

¿Eliminar cadenas de varias líneas?

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;

Linux
  1. Eliminar archivos recursivamente de forma segura:trituración

  2. Opción Rm para fallar en archivos inexistentes?

  3. ¿Automatizar cadenas de escritura en Xmacro?

  4. Reemplazo de sed multilínea

  5. Eliminar información exif por lotes

Bash concatenar cadenas

Cómo comparar cadenas en Bash

Cómo eliminar líneas en Vim / Vi

Cómo usar bash if -z y if -n para probar cadenas en Linux

/dev/null en Linux

Instale PowerDNS en Ubuntu 18.04, 20.04 y 22.04