Encaixe:
grep a file | grep b | grep c
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.
Encaixe:
grep a file | grep b | grep c
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
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.
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
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
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
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
if [ $(grep a file | grep b | grep -c c) -eq $(wc -l file | cut -f1 -d' ') ]; then
echo yes
else
echo no
fi
Tags grep text-processing