Comment exécuter grep avec plusieurs modèles AND?

86

Je voudrais obtenir la correspondance de modèle multiple avec AND implicite entre les modèles, c'est-à-dire l'équivalent d'exécuter plusieurs greps dans une séquence:

grep pattern1 | grep pattern2 | ...

Alors, comment le convertir en quelque chose comme?

grep pattern1 & pattern2 & pattern3

Je voudrais utiliser un seul grep car je construis des arguments de manière dynamique, donc tout doit tenir dans une chaîne. Utiliser filter est une fonctionnalité système, pas grep, donc ce n’est pas un argument.


Ne confondez pas cette question avec:

grep "pattern1\|pattern2\|..."

Ceci est une correspondance multi-motifs OU .

greenoldman
la source

Réponses:

79

agrep peut le faire avec cette syntaxe:

agrep 'pattern1;pattern2'

Avec GNU grep, une fois construit avec le support PCRE, vous pouvez faire:

grep -P '^(?=.*pattern1)(?=.*pattern2)'

Avec astgrep :

grep -X '.*pattern1.*&.*pattern2.*'

(ajout .*s comme <x>&<y>les chaînes de caractères qui correspondent à la fois <x>et <y> exactement , a&bne correspondra jamais à car il n'y a pas une telle chaîne qui peut être à la fois aet ben même temps).

Si les motifs ne se chevauchent pas, vous pourrez peut-être aussi:

grep -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'

Le meilleur moyen portable est probablement avec awkcomme déjà mentionné:

awk '/pattern1/ && /pattern2/'

Avec sed:

sed -e '/pattern1/!d' -e '/pattern2/!d'

Veuillez noter que tous ceux-ci auront une syntaxe d'expression régulière différente.

Stéphane Chazelas
la source
1
La agrepsyntaxe ne fonctionne pas pour moi ... dans quelle version a-t-elle été introduite?
Raman
@Raman 2.04 de 1992 l' avait déjà. Je n'ai aucune raison de croire que ce n'était pas là depuis le début. Des versions plus récentes (après 1992) de agreppeuvent être trouvées incluses avec aperçu / webglimpse . Peut-être que vous avez une implémentation différente. J'ai eu une erreur pour la version ast-grep cependant, l'option pour les regexps augmentés est -X, pas -A.
Stéphane Chazelas
@ StéphaneChazelas Merci, j'ai agrep0.8.0 sur Fedora 23. Cela semble être différent agrepde celui que vous avez mentionné.
Raman
1
@Raman, le tien ressemble à TREagrep .
Stéphane Chazelas
2
@Techiee, ou justeawk '/p1/ && /p2/ {n++}; END {print 0+n}'
Stéphane Chazelas Le
19

Vous n'avez pas spécifié la version de grep, c'est important. Certains moteurs d’expression rationnelle autorisent plusieurs correspondances groupées par AND en utilisant '&', mais il s’agit d’une fonctionnalité non standard et non portable. Mais au moins GNU, grep ne le supporte pas.

OTOH vous pouvez simplement remplacer grep par sed, awk, perl, etc. (énumérés par ordre croissant de poids). Avec awk, la commande ressemblerait à

awk '/ regexp1 / && / regexp2 / && / regexp3 / {print; } '

et il peut être construit pour être spécifié en ligne de commande de manière simple.

Netch
la source
3
Rappelez-vous simplement que l’ awkutilisation des ERE, par exemple l’équivalent de grep -E, est différente de celle du BRE grep.
jw013
3
awkLes regex de sont appelés ERE, mais en fait, ils sont un peu idiosyncratiques. Voici probablement plus de détails que ceux qui s'en soucient
dubiousjim
Merci, grep 2.7.3 (openSUSE). Je vous ai voté, mais je vais laisser la question ouverte pendant un moment, peut-être y a-t-il un truc pour Grep (non que je n'aime pas awk, tout simplement en sachant plus, c'est mieux).
greenoldman
2
L'action par défaut consiste à imprimer la ligne correspondante afin que la { print; }pièce ne soit pas vraiment nécessaire ou utile ici.
triplee
7

Si patternscontient un motif par ligne, vous pouvez faire quelque chose comme ceci:

awk 'NR==FNR{a[$0];next}{for(i in a)if($0!~i)next}1' patterns -

Ou cela correspond à des sous-chaînes au lieu d'expressions régulières:

awk 'NR==FNR{a[$0];next}{for(i in a)if(!index($0,i))next}1' patterns -

Pour imprimer toutes les entrées au lieu de pas de lignes dans le cas patternsvide, remplacer NR==FNRpar FILENAME==ARGV[1]ou par ARGIND==1dans gawk.

Ces fonctions impriment les lignes de STDIN contenant chaque chaîne spécifiée en tant qu'argument en tant que sous-chaîne. gasignifie grep all et gaiignore la casse.

ga(){ awk 'FILENAME==ARGV[1]{a[$0];next}{for(i in a)if(!index($0,i))next}1' <(printf %s\\n "$@") -; }
gai(){ awk 'FILENAME==ARGV[1]{a[tolower($0)];next}{for(i in a)if(!index(tolower($0),i))next}1' <(printf %s\\n "$@") -; }
nisetama
la source
7

Ce n'est pas une très bonne solution mais illustre un "truc" plutôt cool

function chained-grep {
    local pattern="$1"
    if [[ -z "$pattern" ]]; then
        cat
        return
    fi    

    shift
    grep -- "$pattern" | chained-grep "$@"
}

cat something | chained-grep all patterns must match order but matter dont
olejorgenb
la source
1
Utilisez l'un chained-grep()ou l' autre ou function chained-greppas function chained-grep(): unix.stackexchange.com/questions/73750/…
nisetama
3

git grep

Voici la syntaxe qui consiste à git grepcombiner plusieurs modèles à l' aide d' expressions booléennes :

git grep --no-index -e pattern1 --and -e pattern2 --and -e pattern3

La commande ci-dessus imprimera des lignes correspondant à tous les motifs à la fois.

--no-index Rechercher dans le répertoire en cours des fichiers qui ne sont pas gérés par Git.

Vérifier l' man git-grepaide.

Voir également:

Pour le fonctionnement OR , voir:

Kenorb
la source
1

ripgrep

Voici l'exemple utilisant rg:

rg -N '(?P<p1>.*pattern1.*)(?P<p2>.*pattern2.*)(?P<p3>.*pattern3.*)' file.txt

C’est l’un des outils de recherche les plus rapides, car il est construit sur le moteur de regex de Rust qui utilise des automates finis, SIMD et des optimisations littérales agressives pour rendre la recherche très rapide.

Voir également la demande de fonctionnalités associée sur GH-875 .

Kenorb
la source
1

Voici ma prise, et cela fonctionne pour les mots sur plusieurs lignes:

Utilisez find . -type fsuivi du maximum
-exec grep -q 'first_word' {} \;
et du dernier mot clé avec
-exec grep -l 'nth_word' {} \;

-q
-lfichiers de spectacle silencieux / silencieux avec correspondances

La liste suivante retourne la liste des noms de fichiers avec les mots "lapin" et "trou":
find . -type f -exec grep -q 'rabbit' {} \; -exec grep -l 'hole' {} \;

StackRover
la source
-2

Pour rechercher TOUS les mots (ou modèles), vous pouvez exécuter grep dans la boucle FOR . Le principal avantage ici est la recherche dans une liste de regex .

EDITER ma réponse avec un exemple réel:

# search_all_regex_and_error_if_missing.sh 

find_list="\
^a+$ \
^b+$ \
^h+$ \
^d+$ \
"

for item in $find_list; do
   if grep -E "$item" file_to_search_within.txt 
   then
       echo "$item found in file."
   else
       echo "Error: $item not found in file. Exiting!"
       exit 1
   fi
done

Maintenant exécutons-le sur ce fichier:

hhhhhhhhhh

aaaaaaa

bbbbbbbbb

Ababbabaabbaaa

ccccccc

dsfsdf

bbbb

cccdd

aa

caa

# ./search_all_regex_and_error_if_missing.sh

aaaaaaa aa

^ a + $ trouvé dans le fichier.

bbbbbbbbb bbbb

^ b + $ trouvé dans le fichier.

hhhhhhhhhh

^ h + $ trouvé dans le fichier.

Erreur: ^ d + $ introuvable dans le fichier. En sortant!

Noam Manos
la source
1
Votre logique est défectueuse - j'ai demandé à l' ALLopérateur, votre code fonctionne comme ORopérateur, pas AND. Et d'ailleurs. pour cela ( OR) est une solution beaucoup plus facile étant donné directement dans la question.
greenoldman
@greenoldman La logique est simple: la boucle for mettra en boucle TOUS les mots / modèles de la liste et, si elle se trouve dans un fichier, l'imprimera. Donc, supprimez le reste si vous n'avez pas besoin d'action si aucun mot n'a été trouvé.
Noam Manos
1
Je comprends votre logique ainsi que ma question - je parlais d’ ANDopérateur, ce qui signifie que le fichier n’est un succès que s’il correspond au modèle A, au modèle B et au modèle C et ... ANDDans votre cas, le résultat est positif s’il correspond. motif A ou motif B ou ... Voyez-vous la différence maintenant?
greenoldman
@greenoldman ne sait pas pourquoi vous pensez que cette boucle ne vérifie pas ET la condition de tous les modèles? J'ai donc modifié ma réponse avec un exemple concret: il recherchera dans le fichier toutes les expressions rationnelles de la liste, et sur le premier manquant, il se terminera par une erreur.
Noam Manos
Vous l'avez sous les yeux, vous avez une correspondance positive juste après le premier match. Vous devriez avoir "collecter" tous les résultats et les calculer AND. Ensuite, vous devez réécrire le script pour qu’il s’exécute sur plusieurs fichiers. Vous réaliserez peut-être que la question a déjà été répondue et que votre tentative n’apporte rien, désolée.
greenoldman