Questão Como grep uma seção de um arquivo no shell bash


Como posso "grep" as linhas entre uma ocorrência de alguma string1 e a (Nth) ocorrência de alguma string2.

por exemplo.

se o arquivo tiver linha:

UMA
B
C
D
E

F
G
B
C
E

Q

Eu quero colocar as linhas em negrito (aquelas que começam com um B e terminam com um E).

Isso pode ser feito usando o grep? ou alguma outra ferramenta de linha de comando Unix?


4


origem


Isso soa como um gosto para sed. No entanto, meu sed-fu não é tão bom quanto o de Nicole, então estou apenas apontando para um jogo próximo. superuser.com/questions/513393/converting-strings-in-input-file - Hennes


Respostas:


grep não é bem adequado para esta tarefa, você precisa ir uma ferramenta "up":

sed -n '/^B/,/^E/p' infile

Saída:

B
C
D
E
B
C
E

Com relação ao enésimo requisito, eu acho que é mais fácil se você avançar novamente uma ferramenta "up", ou seja, awk:

awk '/^B/ { f = 1; n++ } f && n == wanted; /^E/ { f = 0 }' wanted=2 infile

Saída:

B
C
E

A bandeira f será definido quando /^B/ é encontrado e desfeito quando /^E/ ocorre da mesma forma que a notação sed funciona. n mantém um registro de quantos blocos passaram e quando f == 1 && n == wanted é verdade, o bloco padrão será executado ({ print $0 }).


8



Você pode por favor explicar o que exatamente isso faz? Eu entendo que enviar passa por um fluxo de entrada, eu estou supondo que a sintaxe aqui significa algo como (não imprime) [- n] mas imprime [p] tudo de um regexp / ^ B / para [,] regexp / ^ E /? - epeleg
Existe uma maneira de injetar algum separador entre esses dois blocos encontrados? - epeleg
@epeleg: o exemplo sed funciona como você supõe, todos os comandos seguindo o intervalo são executados enquanto a entrada está nesse intervalo. É um pouco complicado para inserir separadores com sed, prefiro usar awk vez, ver a última edição. - Thor


@ Thor's sed comando não pode ser derrotado, mas com o seguinte perl script eu tento abordar a parte da sua pergunta entre parênteses: "... a (Nth) ocorrência ...".

Uso:

./script <start-regex> <end-regex> [N]

Exemplos com o arquivo em sua pergunta:

$ ./script "B" "E" < examplefile
B
C
D
E
B
C
E

$ ./script "B" "E" 2 < examplefile
B
C
D
E
F
G
B
C
E

Não há verificação de erros ou qualquer outra coisa e o script não é ganancioso, ou seja, de A B C D E E F só B C D E será arredondado com N = 1.


#!/usr/bin/perl

if ($ARGV[2] != "") { $n = $ARGV[2] } else { $n = 1 }
$begin_str = $ARGV[0];
$end_str = $ARGV[1];

while(<STDIN>) {
  if($_ =~ $begin_str) { $flag=1 }             # beginning of match, set flag    
  if($_ =~ $end_str && $flag eq 1) { $i++ }    # i-th occurence of end string

  if($i eq $n) {                               # end of match after n occurences of end string
    $flag=2;
    $i=0; 
  }

  if ($flag ge 1) {                            # append currrent line to matching part
    $out.=$_;
  }

  if($flag eq 2) {                             # after detection of end of match, print complete match
    print $out;
    # print "---\n";                           # separator after a match
    $out="";
    $flag=0;
  }

}

2



Obrigado. Eu poderia usar isso também no futuro, mas por enquanto eu acredito que a solução sed é o meu caminho a percorrer. - epeleg
olhando para este código novamente, eu realmente gosto porque eu posso modificá-lo para atender às minhas necessidades exatas. (como talvez acrescentar o número da iteração no início de cada linha, por exemplo). - epeleg
Você pode explicar por que você está concatenando para $ out em vez de apenas imprimir linha por linha? - epeleg
@epeleg: Você não pode imprimir linha por linha (eu tive isso no começo também), porque então a sequência A B D vai resultar em B D, embora não haja "terminator final" E. Além disso, isso oferece uma maneira fácil de colocar um "separador entre esses dois blocos encontrados" (incluí isso como um comentário no script agora). - mpy
bom ponto sobre o "final não final". obrigado. - epeleg