Remove todas as linhas antes da primeira linha que contém uma correspondência?

3

Usando a string regexp, como posso remover todas as linhas antes da primeira linha que contém uma correspondência? Por exemplo, como posso alterar isso:

lost
load
linux
loan
linux

para isso:

linux
loan
linux

Eu tentei:

echo "lost
load
linux
loan
linux" | sed -e 's/.*^li.*$//g'

mas retorna isso, não mudando nada:

lost
load
linux
loan
linux

Gostaria de fazer com que funcionasse para que não saísse nada quando não houver correspondência.

    
por stacko 22.01.2016 / 19:40

3 respostas

11

Uma maneira, POSIXly:

$ echo "lost
load
linux
loan
linux" | sed -e/linux/\{ -e:1 -en\;b1 -e\} -ed

ou menor:

sed -n '/linux/,$p'

ou ainda mais curto:

sed '/linux/,$!d'

Para os leitores que se perguntam por que prefiro o mais longo em relação à versão mais curta, a versão mais longa executará apenas i / o sobre o restante do arquivo, enquanto usar intervalos pode afetar o desempenho se o segundo endereço for um regex estão tentando ser correspondidos mais do que o necessário.

Considere:

$ time seq 1000000 | sed -ne '/^1$/{' -e:1 -en\;b1 -e\}
=====
JOB sed -e '/^1$/,$d'
87%    cpu
0.11s real
0.10s user
0.00s sys

com:

$ time seq 1000000 | sed -e '/^1$/,/1000000/d'
=====
JOB sed -e '/^1$/,/1000000/d'
96%    cpu
0.24s real
0.23s user
0.00s sys

você pode ver as diferenças entre as duas versões. Com regex complexo, será uma grande diferença.

    
por 22.01.2016 / 20:00
1

Duas outras soluções do awk:

Ambos definem um sinal found ao ver a primeira correspondência de expressão regular e imprimem quando esse sinalizador está definido.

echo "lost
load
linux
loan
linux" | awk 'BEGIN {found = 0} {if (found || $0 ~ /linux/) {found = 1; print}}'

Este é um pouco mais longo, mas não define o found novamente.

echo "lost
load
linux
loan
linux" | awk 'BEGIN {found = 0} {if (found) {print} else if ($0 ~ /linux/) {found = 1; print}}'
    
por 10.12.2017 / 03:06
0

Isso é fácil de fazer com clareza em awk :

echo "lost
load
linux
loan
linux" | awk '
    /^li/ { found = 1 }
    found { print }'

Aqui found é uma variável com um nome auto-explicativo escolhido arbitrariamente. É definido quando o programa encontra uma linha de entrada que corresponde ao regexp. (Variáveis inicialmente padrão para nulo, que é funcionalmente equivalente a 0 ou FALSE.) Assim, as linhas de entrada são impressas depois que o padrão ^li é correspondido, e não antes. A terceira linha da entrada (a primeira linux line) é impressa porque a declaração de impressão condicional vem depois a declaração que procura o padrão e define o sinalizador. Se você quiser começar a imprimir com a quarta linha (a linha depois primeiro linux line), basta inverter a ordem das duas declarações.

Se nenhuma linha de entrada corresponder ao regexp, a bandeira nunca é definida e nada é impresso.

Como eu disse, o nome da variável flag é arbitrário; você pode usar algo mais curto (por exemplo, f ), se desejar. E { print } é a ação padrão, então você pode deixar de fora. Então, se você não se importa com clareza, pode encurtar o texto acima para

echo "lost
load
linux
loan
linux" | awk '/^li/{f=1}f'
    
por 24.01.2016 / 00:34