Como ler certas linhas depois de encontrar algum texto?

12

Como posso ler um certo número de linhas depois de encontrar algum texto?

Por exemplo.:

Leia as próximas 2 linhas depois de encontrar "Unix" em:

Test 1
Test 2
Test 3
Test 4
UNIX
Test 5
Test 6
Test 7
Test 8
Test 9

O resultado pode ser:

Test 5
Test 6

Nota: O "Unix" no último exemplo é um argumento e, portanto, pode ser qualquer outro texto.

O que eu tenho:

Ainda estou sem ideias, preciso apenas de uma luz. Pensando em criar outro script para fazer isso.

Frio
fonte

Respostas:

10

Uma awksolução:

$ awk '$0 == "UNIX" {i=1;next};i && i++ <= 2' file
Test 5
Test 6

Explicação

  • /^UNIX$/{i=1;next}: se virmos UNIX, definimos variável i = 1, processando para a próxima entrada.

  • Se a variável ifor configurada (o que vimos UNIX), i && i++ <= 2apenas será avaliado como valor verdadeiro nas próximas duas linhas depois UNIX, causando a awkação padrão executada print $0.

  • Antes de ver UNIX, inão era definido e começava na terceira linha depois UNIX, ipossuía um valor maior que 2, o que torna a expressão i && i++ <= 2avaliada como falsa, causando awknão fazer nada.

cuonglm
fonte
Após o teste, a solução está recebendo a seguinte mensagem de erro: systax de erro próximo à linha 1 saindo próximo à linha 1
Frio
@ Cold: O que você correu? Observe que o $sinal no início da minha resposta é um prompt de shell, não parte do awkcomando.
cuonglm
Outra variante:awk '/^UNIX$/ {s=NR;next} s && NR<=s+2'
musiphil 15/10
Eu sei que @cuonglm
Cold
@ Cold: Qual é o seu sistema operacional?
cuonglm
12

Uma grepsolução:

grep -A2 -P '^UNIX$' file

Explicação: -A significa: imprima as próximas duas linhas após a correspondência

Ou awk:

awk '$0=="UNIX"{getline; print; getline; print}' file

Explicação: Essa instrução procura pelo UNIX na linha ( $0=="UNIX"). Se isso for dado, ele obtém o próximo próximo no buffer ( getline) e imprime o buffer ( print). Isso é feito duas vezes.

Ou use sed:

sed -n '/^UNIX$/{n;p;n;p}' file

Explicação: Isso procura pelo UNIX ( /^UNIX$/). Se isso for encontrado, ele executará a parte no {...}. nsignifica próximo, psignifica imprimir. Isso é feito duas vezes também.

caos
fonte
Obrigado @chaos, tentarei as 2 últimas opções que você fornecer. Por favor, acrescente alguma explicação de cada opção, não vou entender e fazer.
Fria
Se o número de linhas mudar, quantas alterações farei nas duas últimas opções? Obrigado
Resfriado
@ Cold ver minha edição. Para alterar o número, se as linhas repetirem a getline; print;parte na awkdeclaração ou a n;p;parte na seddeclaração.
caos
Obrigado @chaos, mas quanto maior o número de linhas, maior a expressão e a alteração não é viável na minha opinião. Você não acha? Se 100 linhas?
Frio
@ Cold Então eu usaria a solução grep com grep -A100 -P '^UNIX$' file | tail -n +2. A parte da cauda é para remover o primeiro penhor. Nos outros (sed, awk), você teria que escrever loops, o que torna menos simples.
caos
4
grep -A 2 UNIX file.txt

A página de manual do grep descreve a opção assim:

  -A NUM, --after-context=NUM
      Print NUM  lines  of  trailing  context  after  matching  lines.
      Places  a  line  containing  --  between  contiguous  groups  of
      matches.
Twinkles
fonte
Oi @ Twinkles, boa resposta, mas meu grep tem apenas essas opções "hblcnsviw". Mas a lógica é boa. graças
Fria
Isso também será impresso UNIXna saída.
cuonglm
Para omitir o UNIX, canalizá-lo para tail: [...] | tail -n +1, ou sed: [...] | sed '1d'.
DopeGhoti
1
@DopeGhoti: suas sugestões taile sed '1d'funcionam apenas corretamente se UNIXaparecer apenas uma vez no texto de entrada. Todas as outras respostas permitem múltiplas ocorrências. Talvez seja melhor sugerir ... | grep -v UNIX. É certo que isso fica confuso se UNIXaparecer nas linhas 15 e 17.
G-Man diz 'Reinstate Monica'
Bons pontos. Eu tenho certeza que isso poderia ser feito sedcom alguma forma de sed '/UNIX/d;n;n;p/' /path/to/file, que eu acabei de ler e enviar como resposta.
DopeGhoti
0

Isso parece fazer o truque bem:

sed -n '/UNIX/{n;p;n;p}' /path/to/file

Prova de conceito:

$ for i in {1..9}; do echo $i; done | sed -n '/4/{n;p;n;p}'
5
6
DopeGhoti
fonte
1
O subshell em torno do seu forloop não é necessário.
Pausado até novo aviso.
De fato não é; era um resquício de alguma outra fantasia que eu estava no meio daquela concha no início do dia. Parens removido.
DopeGhoti
0

Você pode usar ex:

ex -s +'1,/UNIX/d|%p|q!' file_or_/dev/stdin

Onde:

kenorb
fonte