Multi-Line Sed Replace

7

Considere o seguinte texto (incidentalmente, parte de um dump MySQL):

CREATE TABLE 'table' (
  'id' int(10) NOT NULL auto_increment,
  'name' varchar(100) NOT NULL default '',
  'description' text NOT NULL,
  PRIMARY KEY  ('id'),
  FULLTEXT KEY 'full_index' ('name')
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

Eu gostaria de remover a chave FULLTEXT e também quero remover a vírgula final na linha acima para que a SQL permaneça válida.

Alguém pode inventar (e explicar) uma receita sed para fazer isso?

    
por Craig Watson 21.08.2013 / 18:19

2 respostas

11

Resposta do AWK

Com o texto de exemplo em um arquivo chamado sql , o seguinte padrão (com quebras de linha e recuo para maior clareza):

awk -v skip=1 '{
    if (skip) { skip=0 }
    else {
        if (/FULLTEXT KEY/) { skip=1; sub(/,$/, "", prevline) }
        print prevline
    }
    prevline=$0
}
END { print prevline }' sql

produz:

CREATE TABLE 'table' (
  'id' int(10) NOT NULL auto_increment,
  'name' varchar(100) NOT NULL default '',
  'description' text NOT NULL,
  PRIMARY KEY  ('id')
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

Explicação:

  • Implementamos "lookahead" apenas imprimindo a linha anterior encontrada em cada iteração, após inspecionar a linha atual.
  • Se a linha atual contiver o marcador FULLTEXT KEY , definimos um sinalizador para ignorar a impressão dessa linha durante a próxima iteração. Também removemos a vírgula à direita na linha anterior que está prestes a ser impressa.
  • Ignoramos a impressão de uma linha inicial vazia (antes que prevline tenha sido definido) definindo inicialmente skip para 1 ("true").
  • Certificamos-nos de imprimir a última linha finalizando o script com uma impressão prevline extra. Observe que a implementação atual pressupõe que essa última linha não é uma linha com risco de ser ignorada, ou seja, que ela não contém o marcador FULLTEXT KEY .

Original (incompleto) sed answer

Esta resposta está incompleta e certamente na maioria dos casos incorreta, pois sed consumirá o fluxo de entrada muito rapidamente para o resultado pretendido ao fazer a correspondência de múltiplas linhas - como apontado nos comentários, ele só funcionará para combina em linhas pares numeradas! sed não tem a funcionalidade lookahead "true", então seria melhor usar Python / Perl / etc., ou AWK como acima.

Com seu texto de amostra em um arquivo chamado sql , o seguinte padrão:

$ sed 'N; s/,\n  FULLTEXT.*//' sql

produz:

CREATE TABLE 'table' (
  'id' int(10) NOT NULL auto_increment,
  'name' varchar(100) NOT NULL default '',
  'description' text NOT NULL,
  PRIMARY KEY  ('id')
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

Explicação:

  • N ativa a correspondência de várias linhas.
  • \n representa uma quebra de linha.
  • s/pattern/replacement/ é a sintaxe de substituição padrão.
  • .* corresponderá a qualquer coisa ao final da linha atual.
por 21.08.2013 / 18:27
0

Gerenciando duas linhas com sed não é tão difícil.
Apenas mantenha duas linhas no espaço padrão.

  • $! N: este comando acrescenta uma linha no espaço padrão.
  • P: imprime a primeira linha no espaço padrão
  • D: exclui a primeira linha no espaço padrão e inicia um novo ciclo (sem ler uma linha)
    se permanecer apenas uma linha, então se comporta como o comando "d" (que é lido uma linha e inicia um novo ciclo)

sed -n '$!N; s/,[[:space:]]*FULLTEXT KEY.*// ;P;D' 
    
por 14.01.2017 / 22:11