Mantenha apenas a primeira linha de cada sequência de linhas consecutivas que correspondam a um padrão

4

Se 2 ou mais linhas consecutivas contiverem um padrão específico, exclua todas as linhas correspondentes e mantenha apenas a primeira linha.

No exemplo abaixo, quando 2 ou mais linhas consecutivas contêm "IO lógico", precisamos excluir todas as linhas correspondentes, mas manter a primeira linha.

Arquivo de entrada:

select * from test1 where 1=1
testing logical IO 24
select * from test2 where condition=4
parsing logical IO 45
testing logical IO 500
handling logical IO 49
select * from test5 where 1=1
testing logical IO 24
select * from test5 where condition=78
parsing logical IO 346
testing logical IO 12

Arquivo de saída:

select * from test1 where 1=1
testing logical IO 24
select * from test2 where condition=4
parsing logical IO 45
select * from test5 where 1=1
testing logical IO 24
select * from test5 where condition=78
parsing logical IO 346
    
por ekassis 05.09.2016 / 12:31

3 respostas

8

Usando awk :

awk '/logical IO/ {if (!seen) {print; seen=1}; next}; {print; seen=0}' file.txt 
  • /logical IO/ {if (!seen) {print; seen=1}; next} verifica se a linha contém logical IO , se encontrada e a variável seen é falsa, ou seja, a linha anterior não contém logical IO , imprima a linha, defina seen=1 e vá para a próxima linha vai para a próxima linha, pois a linha anterior tem logical IO

  • Para qualquer outra linha, {print; seen=0} , imprime a linha e os conjuntos seen=0

Exemplo:

$ cat file.txt 
select * from test1 where 1=1
testing logical IO 24
select * from test2 where condition=4
parsing logical IO 45
testing logical IO 500
select * from test5 where 1=1
testing logical IO 24
select * from test5 where condition=78
parsing logical IO 346
parsing logical IO 346
testing logical IO 12

$ awk '/logical IO/ {if (!seen) {print; seen=1}; next}; {print; seen=0}' file.txt 
select * from test1 where 1=1
testing logical IO 24
select * from test2 where condition=4
parsing logical IO 45
select * from test5 where 1=1
testing logical IO 24
select * from test5 where condition=78
parsing logical IO 346
    
por 05.09.2016 / 12:49
3

com sed :

sed '/logical IO/{x;//!{g;p;};d;};//!h' infile

como funciona:

sed '/logical IO/{         # if line matches
x                          # exchange hold space w. pattern space
//!{                       # if whatever was in the hold buffer doesn't match
g                          # overwrite pattern space with hold space content
p                          # print current pattern space
}
d                          # delete
}
//!h                       # if line doesn't match, copy over the hold space
' infile
    
por 05.09.2016 / 12:58
0

Na linguagem TXR , podemos expressar isso sem nenhuma variável de estado em mutação. Em qualquer posição dada no arquivo, podemos realizar uma combinação de padrão de várias linhas com duas alternativas de ramificação: ou combinamos uma ou mais linhas consecutivas que contêm a sequência de pesquisa e, em seguida, imprimimos a primeira ou então combinamos uma linha e a imprimimos. Uma maneira possível é esta:

@(repeat)
@  (cases)
@    (collect :gap 0 :mintimes 1)
@line
@      (require (search-str line "logical IO"))
@    (end)
@    (do (put-line (first line)))
@  (or)
@line
@    (do (put-line line))
@  (end)
@(end)

Executar:

$ txr first-log-IO.txr data
select * from test1 where 1=1
testing logical IO 24
select * from test2 where condition=4
parsing logical IO 45
select * from test5 where 1=1
testing logical IO 24
select * from test5 where condition=78
parsing logical IO 346

@(repeat) estabelece um passeio pelos dados sem coletar ligações de variável; quando esta construção é vista, geralmente indica que algum efeito colateral ocorre na iteração. Neste caso, é a saída.

Dentro do @(repeat) , temos uma combinação de @(cases) : correspondência multidirecional que consiste em casos separados por @(or) . A segunda ramificação, o caso de fallback, é apenas @line que corresponde a uma linha. A diretiva @(do (put-line (first line))) que segue imprime essa linha.

A ramificação principal do @(cases) coleta material via @(collect) . As correspondências devem ser consecutivas, exigidas por :gap 0 , e deve haver pelo menos uma, exigida por :mintimes 1 . O corpo da coleta corresponde a uma única linha, vinculada à variável line . Em seguida, há uma asserção @(require ...) que falha a menos que a linha contenha a substring "logical IO" . A coleção, portanto, será interrompida quando encontrar uma linha não correspondente, porque o :gap 0 impede que ela seja ignorada. As linhas correspondentes são coletadas implicitamente em uma lista chamada line , que aparece fora do collect (uma variável vinculada dentro de uma coleta automaticamente se torna uma lista fora da coleta, de todos os valores vinculados às várias iterações). Acabamos de imprimir o primeiro, conforme necessário, suprimindo o resto.

Observe que as duas correspondências @line não têm nada a ver uma com a outra; eles ligam a variável line em escopos diferentes.

Outra maneira é fazer alguma programação funcional em relação a lazy lists no TXR Lisp:

[(opip (partition-by (do cond
                       ((search-str @1 "logical IO") t)
                       (t @1)))
       (mapcar* first)
       put-lines)
 (get-lines)]
$ txr first-log-IO.tl

O operador opip é um açúcar sintático para construir um pipeline de funções. Seus argumentos são tratados como op syntax: uma macro para gerar funções anônimas com argumentos numerados e implícitos.

A forma geral é [(opip ...) (get-lines)] que significa apenas "chamar a função produzida por opip , com o resultado de (get-lines) como argumento". Este (get-lines) converte o fluxo de entrada padrão em uma lista lenta de cadeias de caracteres. (Seu "oposto" é put-lines , que faz uma aparição).

Agora, no pipeline, usamos partition-by para (preguiçosamente!) converter a lista de linhas em uma lista de listas que são suas partições. A condição de particionamento é tal que cada linha que contém logical IO é mapeada para o símbolo t e todas as outras linhas apenas mapeiam para si mesmas. Isso significa que as linhas consecutivas que contêm logical IO aparecem como uma partição e todas as outras linhas aparecem isoladas como partições de comprimento um. Tudo o que temos a fazer com esses dados agora é mapear cada partição para seu primeiro item, via (mapcar* first) e passá-la para put-lines para despejar o resultado.

Usamos mapcar* porque essa é a versão lenta de mapcar . Queremos que tudo seja preguiçoso para que a ação seja realmente acionada por put-lines . À medida que put-lines percorre a lista de saída, ela extrai itens do preguiçoso mapcar* , o que força os elementos produzidos pelo partition-by , o que força a lista produzida por (get-lines) , o que faz com que a E / S ocorra leia essas linhas.

Se usarmos o mapcar regular por engano, causaremos o problema de que toda a saída seja construída na memória antes de ser descartada, o que é um mau presságio para arquivos grandes.

    
por 06.09.2016 / 03:25