Grep cherche deux mots dans une ligne


46

J'ai essayé de trouver un moyen de filtrer une ligne contenant les mots "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.


1
Pour rechercher toutes les chaînes d'un fichier, vous pouvez exécuter grep dans la boucle FOR: unix.stackexchange.com/a/462445/43233
Noam Manos

Réponses:


62

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

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

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

Si vous voulez utiliser des expressions rationnelles normales au lieu de celles étendues ( -E), vous devez insérer une barre oblique inverse 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

Votre dernière ligne est une conjonction non disjonction non? À savoir: les grep ricelignes de recherche contenant rice. Il est alimenté dans grep lemonlequel vous trouverez uniquement des lignes contenant du citron .. et ainsi de suite. Alors que le PO - ainsi que vos réponses précédentes - autorisent n'importe lequel des [riz | citron | poulet]
javadba le

Version du script: askubuntu.com/a/879253/5696
Jeff

@ Florian Diesch - Vous vous demandez pourquoi il |faut fuir grep? Merci!
Fugitif

1
@fugitive egreputilise l'expression rationnelle étendue où |est comprise comme logique OU. grepLa valeur par défaut est regex basic, où \|est OR
Sergiy Kolodyazhnyy

Comme indiqué dans grepla page de manuel de, egrepest obsolète et doit être remplacé par grep -E. J'ai pris la liberté de modifier la réponse en conséquence.
dessert

26

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


21

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


11

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

grep avec l' option -P (Compatibilité Perl) et une expression positive d'anticipation(?=(regex)) :

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

ou vous pouvez utiliser ci-dessous à la place:

grep -P '(?=.*?rice)(?=.*?lemon)' infile
  • Les .*?moyens d' adaptation des caractères .qui occurrences zéro ou plusieurs fois *alors qu'ils sont en option suivie par un motif ( riceou lemon). Le ?rend tout facultatif avant (signifie zéro ou une fois tout ce qui est assorti .*)

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

Donc, cela retournera toutes les lignes avec contient les deux lemonet ricedans un ordre aléatoire. Cela évitera également d'utiliser |s et doublé greps.


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



1

Si nous admettons qu'il grepest acceptable de fournir une réponse non fondée, tout comme la réponse ci-dessus awk, je proposerais une perlligne simple telle que:

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

La recherche peut être ignorante 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.


Refusé !!! ;) Parce que ça n'a aucun sens .. :)
An0n

0

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 "$@"

1
Cela devrait probablement être implémenté en utilisant une fonction récursive, au lieu de construire une chaîne de commande et de la evalrenvoyer, ce qui casse facilement
muru

@muru N'hésitez pas à suggérer une modification. J'apprécie le commentaire.
Jeff

1
Le modifier fait trop de réécriture, je ne le ferai donc pas. Si vous voulez l'ajouter, voici ce que je suppose qu'il devrait ressembler à: paste.ubuntu.com/23915379
muru
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.