Grep procurando duas palavras em uma linha

46

Eu tenho tentado encontrar uma maneira de filtrar uma linha que contenha a palavra "limão" e "arroz". Eu sei como encontrar "limão" ou "arroz", mas não os dois. Eles não precisam estar próximos um do outro, apenas um na mesma linha de texto.

Sebastian
fonte
1
Para encontrar todas as cordas dentro de um arquivo, você pode executar grep em loop FOR: unix.stackexchange.com/a/462445/43233
Noam Manos

Respostas:

62

"Ambos na mesma linha" significam "'arroz' seguido de caracteres aleatórios seguidos de 'limão' ou o contrário".

Em regex que é rice.*lemonou lemon.*rice. Você pode combinar isso usando um |:

grep -E 'rice.*lemon|lemon.*rice' some_file

Se você deseja usar regex normal em vez de estendidos ( -E), precisará de uma barra invertida antes de |:

grep 'rice.*lemon\|lemon.*rice' some_file

Para mais palavras que rapidamente se tornam um pouco mais longas e geralmente é mais fácil usar várias chamadas grep, por exemplo:

grep rice some_file | grep lemon | grep chicken
Florian Diesch
fonte
Sua última linha é uma conjunção, não disjunção, não? Ou seja: grep riceencontra as linhas que contêm rice. Ele é alimentado no grep lemonqual somente serão encontradas linhas contendo limão ... e assim por diante. Considerando que o OP -, bem como as suas respostas anteriores - estão permitindo que qualquer de [arroz | limão | frango]
javadba
Versão do script: askubuntu.com/a/879253/5696
Jeff
@Florian Diesch - Mente explicando por que |precisa ser evitado grep? Obrigado!
fugitivo
1
@fugitive egrepusa regex estendido, onde |é entendido como lógica OR. grepo padrão é o regex básico, onde \|está OR #
Sergiy Kolodyazhnyy
Conforme declarado na página grepde manual, egrepestá obsoleto e deve ser substituído por grep -E. Tomei a liberdade de editar a resposta de acordo.
dessert
26

Você pode canalizar a saída do primeiro comando grep para outro comando grep e isso corresponderia aos dois padrões. Então, você pode fazer algo como:

grep <first_pattern> <file_name> | grep <second_pattern>

ou,

cat <file_name> | grep <first_pattern> | grep <second_pattern>

Exemplo:

Vamos adicionar alguns conteúdos ao nosso arquivo:

$ echo "This line contains lemon." > test_grep.txt
$ echo "This line contains rice." >> test_grep.txt
$ echo "This line contains both lemon and rice." >> test_grep.txt
$ echo "This line doesn't contain any of them." >> test_grep.txt
$ echo "This line also contains both rice and lemon." >> test_grep.txt

O que o arquivo contém:

$ cat test_grep.txt 
This line contains lemon.
This line contains rice.
This line contains both lemon and rice.
This line doesn't contain any of them.
This line also contains both rice and lemon.

Agora, vamos cumprimentar o que queremos:

$ grep rice test_grep.txt | grep lemon
This line contains both lemon and rice.
This line also contains both rice and lemon.

Nós só obtemos as linhas onde ambos os padrões correspondem. Você pode estender isso e canalizar a saída para outro comando grep para outras correspondências "AND".

Aditya
fonte
21

Embora a pergunta solicite 'grep', achei útil postar uma solução simples 'awk':

awk '/lemon/ && /rice/'

Isso pode ser facilmente estendido com mais palavras ou outras expressões booleanas além de 'e'.

David B.
fonte
11

Outra idéia para encontrar as correspondências em qualquer ordem é usar:

grep com a opção -P (Perl-Compatibility) e regex lookahead positivo(?=(regex)) :

grep -P '(?=.*?lemon)(?=.*?rice)' infile

ou você pode usar abaixo:

grep -P '(?=.*?rice)(?=.*?lemon)' infile
  • O .*?meio que corresponde a qualquer caractere .que ocorra zero ou mais vezes *enquanto opcional, seguido de um padrão ( riceou lemon). O ?faz tudo opcional antes (meio zero ou um tempo de tudo combinado .*)

(?=pattern): Lookahead positivo: o construto lookahead positivo é um par de parênteses, com o parêntese de abertura seguido de um ponto de interrogação e um sinal de igual.

Portanto, isso retornará todas as linhas com contém ambos lemone riceem ordem aleatória. Além disso, isso evitará o uso de |s e o dobro de greps.


Links externos: Tópicos avançados de Grep Lookahead positivo - GREP para designers

αғsнιη
fonte
5
grep -e foo -e goo

Retornará correspondências para foo ou goo

netskink
fonte
1

Se admitirmos que grepé aceitável fornecer uma resposta que não seja baseada, como a resposta acima awk, eu proporia uma perllinha simples como:

$ perl -ne 'print if /lemon/ and /rice/' my_text_file

A pesquisa pode estar ignorando o caso com algumas / todas as palavras como /lemon/i and /rice/i. Na maioria das máquinas Unix / Linux, o perl é instalado e o awk de qualquer maneira.

Gilles Maisonneuve
fonte
Recusou!!! ;) Porque não faz sentido .. :)
An0n 26/08/1918
0

Aqui está um script para automatizar a solução de tubulação grep:

#!/bin/bash

# Use filename if provided as environment variable, or "foo" as default
filename=${filename-foo}

grepand () {
# disable word splitting and globbing
IFS=
set -f
if [[ -n $1 ]]
then
grep -i "$1" ${filename} | filename="" grepand "${@:2}"
else
# If there are no arguments, assume last command in pipe and print everything
cat
fi
}

grepand "$@"
Jeff
fonte
1
Isso provavelmente deve ser implementado usando uma função recursiva, em vez de construir uma cadeia de comando e evaling-lo, o que quebra facilmente
Muru
@muru Sinta-se livre para sugerir uma edição. Eu aprecio o comentário.
Jeff
1
Editá-lo será reescrito, então não farei isso. Se você quiser adicioná-lo, aqui está o que eu imagino que deva ser: paste.ubuntu.com/23915379
muru