Filtra linhas que contêm um número fixo de ocorrências de padrões

6

Suponha que haja um arquivo

foo bar cat dog
foo foo cat bar
bar foo foo foo

Como fazemos grep para linhas com um determinado número de ocorrências de foo ? se o número for 1 , somente a primeira linha do arquivo de amostra deverá ser impressa?

    
por Rahul 15.08.2016 / 20:14

4 respostas

6
$ grep 'foo' file | grep -v 'foo.*foo'

Primeiro, selecione todas as linhas que contenham foo e, em seguida, remova todas as linhas com foo seguido por outro foo em algum lugar na linha.

Se todas as linhas contiverem pelo menos um foo (como no seu exemplo), você poderá pular o primeiro grep .

Para uma solução geral para "Como faço grep para exatamente N ocorrências de uma string?": grep para linhas com pelo menos N correspondências, em seguida, remova as linhas com N + 1 correspondências (ou mais).

    
por 15.08.2016 / 20:21
4

No caso geral - imprima apenas linhas com exatamente as ocorrências N , você pode usar awk gsub() , que retorna o número não. de substituições feitas e imprimir a linha se não. corresponde ao requisito, e. para imprimir linhas com exatamente três ocorrências:

 awk '{l=$0;t=gsub(/foo/,"",l)}t==3' infile

Outra maneira com sed :

sed 's/foo/&/3   
t x
: k
d
: x
s/foo/&/4
t k' infile

Isso tenta substituir a terceira ocorrência por si, se falhar, a linha será d eleted; se for bem sucedido, ele ramifica para : x onde tenta substituir a 4ª ocorrência consigo mesma - se bem sucedida (significa que há mais de 3 ocorrências) ela ramifica para : k (então essa linha também é deletada) senão não faz nada auto-imprimindo a linha ...)

Para o caso específico do seu exemplo (linhas com apenas uma ocorrência), você também pode usar

sed '/foo/!d;/foo.*foo/d' infile

ou algo parecido:

pcregrep '^(?:(?!foo).)*foo((?:(?!foo).)*)$' infile
    
por 16.08.2016 / 00:03
2

Usando grep -c para contar:

while read line; do [[ $(echo $line | sed 's/ /\n/g' | grep -c foo) == 2 ]] && echo "$line"; done < file.txt
    
por 16.08.2016 / 01:01
2

Outra opção pode ser usar perl , por exemplo, inserindo as correspondências em um contexto de lista e testando-as em um contexto escalar:

perl -ne 'my $count = () = $_ =~ /foo/g; print if $count == 1' file

ou sem a variável escalar explícita

perl -ne 'print if ( () = $_ =~ /foo/g ) == 1' file

Este caminho é facilmente generalizado para retornar linhas com qualquer outro número desejado de correspondências.

Veja, por exemplo, Há um atalho Perl para contar o número de correspondências em uma string?

    
por 16.08.2016 / 03:12