Excluir cadeias de várias linhas

3

Houve várias perguntas aqui sobre a substituição de sequências de várias linhas usando o shell do Unix, mas não encontrei nenhuma que funcione nessa situação.

Eu estou tentando remover chaves e restrições de alguma DDL do MySQL, que se parece com isso (um exemplo):

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;

Eu quero remover tudo da vírgula que termina a linha antes de 'PRIMARY KEY' até, mas sem incluir ') ENGINE =' (pode haver zero ou várias linhas entre elas, e elas nem sempre começam com KEY ou ter os parênteses, mas o ') ENGINE =' é consistente. O resultado deve ser assim:

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;

Estou aberto a usar qualquer utilitário de linha de comando padrão (por exemplo, sed, perl, awk), mas como esses arquivos podem ser bem grandes (alguns da ordem de dezenas ou centenas de GB) eles precisam ser eficientes . Uma vez que os arquivos são normalmente armazenados com gzip (ou às vezes eu procuro a saída do utilitário mysql dump diretamente, em vez de gravar primeiro no disco) eu preciso de algo que possa ser inserido e finalizado.

    
por Swechsler 19.10.2017 / 18:01

3 respostas

1

Mantenha o estado sobre se deseja imprimir a linha anterior, edite o dito para remover a vírgula quando necessário. Este método mantém apenas uma ou duas linhas do arquivo na memória.

#!/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;
    
por 19.10.2017 / 19:04
2

Usando ex (também conhecido como vim no modo Ex):

ex +'%s/,\n *PRIM\_.*\ze\n) ENGINE//' +wq file

Apenas uma versão "em lote" do Vim substitute-delete (substituição vazia // ) que combina várias linhas com \_.* e exclui a última parte do padrão com \ze .

Isso modifica o arquivo no local. Se você não quer que faça isso para salvar em um novo arquivo file2 :

ex +'%s/,\n *PRIM\_.*\ze\n) ENGINE//' +'w file2' +q! file

Atualização: para canalizar em um arquivo ... isso é um pouco incomum com a adição de /dev/stdin , mas é o truque:

cat file | ex +'%s/,\n *PRIM\_.*\ze\n) ENGINE//' +'%p|q!' /dev/stdin
    
por 19.10.2017 / 19:22
1

Solução sed GNU baseada em fluxo:

#Unless on the last line, read the next line and append it to the pattern space
$!N

#If the current pair of lines in buffer, matches the "/,\nPRIMARY KEY/" pattern
/,\n\?\s*PRIMARY KEY/ { 
   #Read the following lines, until "/) ENGINE/" pattern is encountered
   :loop
   /) ENGINE/ b exit 
   N 
   b loop 
}

#Strip away everything between ", PRIMARY KEY" and ") ENGINE"
:exit
s/,\n\?\s*PRIMARY KEY.*\() ENGINE\)/\n/

#Print the content of the pattern space up to the first newline (i.e. the first line out of two)
P

#Delete everything up to the first newline (leaving the second line in pattern space buffer)
#and restart the cycle
D

Execute da seguinte forma:

cat data.txt|sed -nf script.sed

(você pode compactar isso em uma linha, removendo comentários e substituindo novas linhas por ";" ).

Versão por @Philippos:

Com alguma simplificação e mais portátil:

sed -e '$!N;/,\n *PRIMARY KEY/!{P;D;};s/,//;:loop' -e 'N;s/ *PRIMARY KEY.*\() ENGINE\)//;T loop'
    
por 19.10.2017 / 21:05