Imprima até (e inclua) a enésima ocorrência de um padrão em uma linha

3

Digamos que eu tenha várias linhas semelhantes abaixo em um arquivo.

Turbo is a cat. cats are good. cats are not dog.
Coco is a black cat. cats are furry. cats are not dog.

agora, se quiser grep all the ^.*cat mas quiser mencionar especialmente para capturar até a primeira (ou n) ocorrência da palavra cat .
Saída desejada:

Turbo is a cat
Coco is a black cat
*blah is a so and so cat*

Como posso grep ?

PS: Eu adoraria ter uma resposta usando apenas grep (ou suas outras variantes).

PS: Eu não quero grep ^.*cat. e, em seguida, faço qualquer operação para remover o "." . Eu quero uma resposta genérica.

    
por Krishna Gupta 12.04.2016 / 22:29

3 respostas

0

Com POSIX grep , você só pode escolher entre imprimir a linha inteira ou não imprimir o conteúdo da linha. Se você quiser transformar a linha, você precisa usar outra ferramenta como sed ou awk. Para imprimir até a primeira ocorrência de cat :

sed -n 's/cat.*/cat/'
awk 'sub(/cat.*/,"")'

Imprimir até a N ocorrência é mais complicado.

sed -n 's/cat/&\
/3; T; P'
awk 'gsub(/cat/,"&\n") >= 3 {split($0, a, "\n"); printf "%s%s%s\n", a[1], a[2], a[3]}'

Com GNU grep , você pode usar a opção -o para imprimir apenas a parte correspondente da linha. Use a opção -P para ativar a sintaxe Perl, para que os quantificadores não-vorazes estejam disponíveis.

grep -P -o '^(.*?cat){1}'

Substitua o número entre chaves pelo número n da última ocorrência de cat a ser impresso.

Embora seja possível expressar a mesma coisa com expressões regulares estendidas ( -E ), isso requer um regexp complexo, cujo tamanho é exponencial no tamanho da parte a ser contada ( cat here).

    
por 13.04.2016 / 02:30
2

grep apenas seleciona linhas com base na expressão regular especificada e as imprime.

Acho que você é forçado a canalizar as linhas de saída e usar um comando adicional para fazer o trabalho.

Normalmente você usa sed ou awk para fazer o trabalho sem grep , porque eles podem selecionar linhas e substituir cadeias de caracteres.

Existe uma solução abaixo usando awk :

awk -v word=cat -v n=2 'BEGIN {wordlength=length(word);} {line=$0;outputline="";position=index(line,word);for (i=1;position>0 && i<=n; i++) { outputline=outputline substr(line,1,position+wordlength-1);line=substr(line,position+wordlength);position=index(line,word);  } if (i!=1) {print outputline;}}'

Você deve definir word para a string a ser pesquisada e o n para o número de ocorrências desejado.

O teste:

$ awk -v word=cat -v n=2 'BEGIN {wordlength=length(word);} {line=$0;outputline="";position=index(line,word);for (i=1;position>0 && i<=n; i++) { outputline=outputline substr(line,1,position+wordlength-1);line=substr(line,position+wordlength);position=index(line,word);  } if (i!=1) {print outputline;}}' file
Turbo is a cat. cat
Coco is a black cat. cat
    
por 12.04.2016 / 22:55
0

Aqui está uma solução sed (por exemplo, imprima até e inclua a segunda ocorrência; substitua 2 pelo seu não):

sed -n 's/cat/&\
/2
t print
d
:print
P' infile

Isso desativa o auto-impressão via -n e tenta substituir a segunda ocorrência de cat por cat + um caractere de nova linha. Se a substituição for bem-sucedida, ela se ramificará para :print e P rints até a nova linha, caso contrário, a linha será d eleted.

Com gnu sed , você pode escrevê-lo como um folheto (por exemplo, imprimir até e incluir a quinta ocorrência):

sed -n 's/cat/&\n/5;tt;d;:t;P' infile
    
por 12.04.2016 / 23:40