web-dev-qa-db-fra.com

Grep cherche deux mots dans une ligne

J'ai essayé de trouver un moyen de filtrer une ligne contenant le mot "citron" et "riz". Je sais comment trouver du "citron" ou du "riz" mais pas les deux. Ils n'ont pas besoin d'être l'un à côté de l'autre, mais sur la même ligne de texte.

44
Sebastian

"Les deux sur la même ligne" signifient "" riz "suivi de caractères aléatoires suivis de" citron "ou inversement".

Dans regex c'est rice.*lemon ou lemon.*rice. Vous pouvez combiner cela en utilisant un |:

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

Si vous souhaitez utiliser des expressions rationnelles normales au lieu de celles étendues (-E), vous devez insérer une barre oblique inversée avant le |:

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

Pour plus de mots qui deviennent rapidement un peu longs et il est généralement plus facile d'utiliser plusieurs appels de grep, par exemple:

grep rice some_file | grep lemon | grep chicken
57
Florian Diesch

Vous pouvez diriger le résultat de la première commande grep vers une autre commande grep, ce qui correspond aux deux modèles. Donc, vous pouvez faire quelque chose comme:

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

ou,

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

Exemple:

Ajoutons du contenu à notre fichier:

$ 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

Que contient le fichier:

$ 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.

Maintenant, passons à ce que nous voulons:

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

Nous n'obtenons que les lignes où les deux modèles correspondent. Vous pouvez étendre cela et diriger la sortie vers une autre commande grep pour d'autres correspondances "AND".

26
Aditya

Bien que la question demande "grep", j'ai pensé qu'il pourrait être utile de poster une solution simple "awk":

awk '/lemon/ && /rice/'

Cela peut facilement être étendu avec plus de mots, ou d'autres expressions booléennes en plus de 'et'.

21
David B.

Une autre idée pour trouver les correspondances dans n'importe quel ordre utilise:

grep avec -P(Compatibilité avec Perl) option et expression régulière avec lookahead (?=(regex)) :

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

ou , vous pouvez utiliser ci-dessous à la place:

grep -P '(?=.*?rice)(?=.*?lemon)' infile
  • .*? signifie que tous les caractères . apparaissent zéro ou plus * tant qu’ils sont facultatifs, suivis d’un motif (rice ou lemon). ? rend tout ce qui est optionnel avant (signifie zéro ou une fois tout ce qui correspond à .*)

(?=pattern): Lookahead positif: la construction lookahead positive est une paire de parenthèses, la parenthèse ouvrante étant suivie d'un point d'interrogation et d'un signe égal.

Ainsi, toutes les lignes contenant lemon et rice seront retournées dans un ordre aléatoire. Cela évitera également d'utiliser |s et de doubler greps.


Liens externes:
Sujets avancés sur Grep
Lookahead positif - GREP pour les concepteurs

11
αғsнιη
grep -e foo -e goo

Rendra les matchs pour foo ou goo

5
netskink

Si nous admettons qu'il est acceptable de fournir une réponse qui n'est pas basée sur grep, comme la réponse ci-dessus basée sur awk, je proposerais une simple ligne Perl telle que:

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

La recherche peut ignorer la casse avec certains/tous les mots tels que /lemon/i and /rice/i. Sur la plupart des machines Unix/Linux, Perl est installé aussi bien que awk.

1
Gilles Maisonneuve

Voici un script pour automatiser la solution de canalisation 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 "$@"
0
Jeff