Somente cat da linha específica X (com um padrão) para outra linha específica Y (com um padrão)

3

Um pequeno problema estendido de " cat linha x para linha y em um arquivo enorme ":

Eu tenho um arquivo enorme (2-3 GB). Eu gostaria de cat / imprimir apenas a partir da linha com "foo:" para a linha com "goo:". Suponha que "foo:" e "goo:" apareçam apenas uma vez em um arquivo; "foo:" continua "goo:".

Até agora, esta é minha abordagem:

  • Primeiro, encontre a linha com "foo:" e "goo:": grep -nr "foo:" bigfile
  • Retorna 123456: foo: hello world! e 654321: goo: good bye!
  • Uma vez que conheço esses números de linha inicial e final, e a diferença (654321-123456 = 530865), posso fazer o gato seletivo:
  • tail -n+123456 bigfile | head -n 530865

A minha pergunta é como posso substituir efetivamente as constantes do número de linha por expressões (por exemplo, grep ...)?

Eu posso escrever um script Python simples, mas quero alcançá-lo usando apenas comandos combinados.

    
por Nullptr 02.09.2014 / 04:23

4 respostas

9

sed -n '/foo/,/goo/p;/goo/q' <bigfile

Isso imprimiria apenas essas linhas. Se você quisesse os números de linha, você adicionaria um = .

sed -n '/foo/=;/goo/=;//q' <bigfile

O q é importante porque q uits a entrada quando é chamada - else sed continuará a ler o arquivo até o final.

Se você não quiser imprimir foo/goo linhas, poderá fazer isso:

Com o GNU sed :

sed -n '/foo/,/goo/!d;//!p;/goo/q
' <<\DATA
line1
foo 
line3
line4
line5
goo 
line7
DATA

OUTPUT

line3
line4
line5

E com qualquer outro:

sed -n '/foo/G;/\n/,/goo/!d;//q;/\n/!p 
' <<\DATA
line1
foo 
line3
line4
line5
goo 
line7
DATA    

OUTPUT

line3
line4
line5

De qualquer forma, no entanto, isso também sai da entrada assim que encontra a última linha da pesquisa.

    
por 02.09.2014 / 04:29
5

Se você está certo em abandonar sua abordagem atual de usar algo em subshells para obter os números de linha e permitir que outro utilitário imprima o arquivo, isso pode ser feito em awk com pouca dificuldade:

Se você deseja imprimir as linhas entre foo: e goo: e não as linhas em si, use o seguinte ( retirado daqui originalmente ):

awk '/goo:/ { exit }; flag; /foo:/ { flag = 1 }' bigFile

O exit s acima quando vir o token final ( goo: ), print s se flag for verdadeiro e definir flag como verdadeiro ( 1 , na verdade) quando atingir o token de abertura ( foo: ).

Se, no entanto, você preferir incluir as linhas de token na saída, o comando será realmente mais simples, como @ jasonwryan mencionado :

awk '/foo:/,/goo:/' bigFile

Se você está obcecado em apenas obter os números de linha e não realmente imprimir o arquivo com o mesmo utilitário, então você pode obter os números de linha dos tokens de início e fim assim:

awk '/foo:|goo:/ { print NR }' bigFile
    
por 02.09.2014 / 04:47
4

Alternativa sed um:

sed '/foo/,$!d;/goo/q'
    
por 02.09.2014 / 10:46
1

Para substituir as constantes por expressões, você pode usar a substituição de comandos .

Para substituir a saída de um comando em uma expressão, use $(command)

Nesse caso, a linha de comando apropriada é:

tail -n+$(grep -nr "foo:" bigfile | cut -d':' -f1) bigfile | \
head -n$(($(grep -nr "goo:" bigfile | cut -d':' -f1)-$(grep -nr "foo:" bigfile | cut -d':' -f1)+1))

Isso imprimirá todas as linhas da linha que contém foo: para a linha que contém goo: , inclusive.

    
por 02.09.2014 / 05:31