Usando 'grep' para encontrar linhas que contenham todos os três caracteres especificados em qualquer ordem [duplicada]

10

Temos um documento que contém linhas e temos que descobrir se [a | b | c] aparece pelo menos uma vez em cada linha, independentemente da ordem.

Por exemplo:

Entrada:

abc
bca
cab
hhfdhdhfabjfdjdjff
acjfdjdfjdf
fhfhfhfabcjdfjdjfk
ahfhfbkjfjdjffc

Saída desejada (a quarta linha está ausente, pois contém apenas a e b , mas não c ):

abc
bca
cab
fhfhfhfabcjdfjdjfk
ahfhfbkjfjdjffc

Estamos usando o Linux.

    
por user101848 04.02.2015 / 14:47

8 respostas

24

Encaixe:

grep a file | grep b | grep c
    
por 04.02.2015 / 14:58
8

A vantagem sed de grep é fácil de ver nesses exemplos

sed -n '/a/{/b/{/c/p;};}' file

ou:

sed '/a/!d;/b/!d;/c/!d' file
    
por 04.02.2015 / 14:59
7

Vamos comparar todas as soluções propostas!

Eu tenho um arquivo de texto test.txt de tamanho ~ 230M. Estou no Mac Mini atualizado para 10.10.

1) awk solution por Hauke Laging (melhor não ...):

$ time bash -c "awk '/a/ && /b/ && /c/' >> /dev/null"
19.51 real        19.23 user         0.20 sys

2) "bruteforced" grep por Raghuraman R e Hauke Laging (melhor, mas não realmente ...):

$ time bash -c "grep -e 'a.*b.*c' -e 'a.*c.*b' -e 'b.*a.*c' -e 'b.*c.*a' -e 'c.*a.*b' -e 'c.*b.*a' test.txt >> /dev/null"
10.02 real         9.93 user         0.07 sys

3) encadeada grep por muru (ok!):

$ time bash -c "grep a test.txt | grep b | grep c >> /dev/null"
1.61 real         3.08 user         0.29 sys

4) perl solution por terdon (melhor ainda!):

$ time bash -c "perl -ne 'print if /a/ && /b/ && /c/' test.txt >> /dev/null"
0.83 real         0.75 user         0.07 sys

Então, eu acho que "grep encadeado" está ok, mas você também pode usar o Perl para um desempenho ainda melhor. Eu não pude testar a abordagem sed , porque o programa fornecido pelo Costas não funciona "como está" no console do mac.

BTW Eu não sou especialista em benchmarking, desculpe se fiz algo errado.

    
por 05.02.2015 / 14:13
6
awk '/a/ && /b/ && /c/' file

Ou com o grep (que não seria bem dimensionado, no entanto):

grep -e 'a.*b.*c' -e 'a.*c.*b' -e 'b.*a.*c' -e 'b.*c.*a' -e 'c.*a.*b' -e 'c.*b.*a'  file
    
por 04.02.2015 / 14:55
5

Através de grep , que aceita o parâmetro -P ( Perl-regexp ).

$ grep -P '^(?=.*a)(?=.*b)(?=.*c)' file
abc
bca
cab
fhfhfhfabcjdfjdjfk
ahfhfbkjfjdjffc

Explicação:

  • ^ Corresponde ao início de uma linha
  • (?=.*a) Apenas se a string a ser correspondida deve conter uma letra a
  • (?=.*b) deve conter b
  • (?=.*c) deve conter c
por 04.02.2015 / 18:15
4

Eu faria isso em perl:

$ perl -ne 'print if /a/ && /b/ && /c/' file 
abc
bca
cab
fhfhfhfabcjdfjdjfk
ahfhfbkjfjdjffc

Se você quiser apenas verificar se cada linha corresponde a todas as três letras (sem imprimir a própria linha), você pode fazer:

$ perl -lne '$k++ if /a/ && /b/ && /c/; 
 END{$k==$. ? print "yes" : print "no"}' file

Ou

$ awk '(/a/ && /b/ && /c/){k++} END{if(k==NR){print "yes"} else{print "no"}}' file
    
por 04.02.2015 / 16:56
3

Se for apenas a, b, c, então podemos usar uma mistura das opções 'grep -o' e 'grep -e' como abaixo

grep -e "a.*b.*c" -e "a.*c.*b" -e "b.*a.*c" -e "b.*c.*a" -e "c.*a.*b" -e "c.*b.*a" file

Você também pode verificar a pergunta já feita em link

    
por 04.02.2015 / 15:01
0
if [ $(grep a file | grep b | grep -c c) -eq $(wc -l file | cut -f1 -d' ') ]; then
    echo yes
else
    echo no
fi
    
por 04.02.2015 / 15:01