web-dev-qa-db-fra.com

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

Je voudrais obtenir la correspondance multi-modèles avec implicitement ET entre les modèles, c'est-à-dire équivalent à 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 grep unique car je construis des arguments dynamiquement, donc tout doit tenir dans une seule chaîne. L'utilisation du filtre est une fonction système, pas grep, donc ce n'est pas un argument pour cela.


Ne confondez pas cette question avec:

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

Il s'agit d'une OU correspondance de plusieurs motifs.

91
greenoldman

agrep peut le faire avec cette syntaxe:

agrep 'pattern1;pattern2'

Avec GNU grep, lorsqu'il est construit avec le support PCRE, vous pouvez faire:

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

Avec ast grep :

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

(ajouter .*s comme <x>&<y> correspond à des chaînes qui correspondent aux deux <x> et <y> exactement , a&b ne correspondrait jamais car il n'y a pas une telle chaîne qui peut être a et b en même temps) .

Si les motifs ne se chevauchent pas, vous pouvez également faire:

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

Le meilleur moyen portable est probablement avec awk comme 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.

85
Stéphane Chazelas

Vous n'avez pas spécifié de version grep, c'est important. Certains moteurs d'expression régulière autorisent plusieurs correspondances regroupées par ET en utilisant "&", mais il s'agit d'une fonctionnalité non standard et non portable. Mais, au moins GNU grep ne prend pas en charge cela.

OTOH, vous pouvez simplement remplacer grep par sed, awk, Perl, etc. (classés par ordre d'augmentation 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.

19
Netch

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
8
olejorgenb

Si patterns contient un modèle 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 lignes de l’entrée au lieu d’aucune ligne dans le cas où patterns est vide, remplacez NR==FNR avec FILENAME==ARGV[1], ou avec ARGIND==1 dans gawk.

Ces fonctions affichent les lignes de STDIN qui contiennent chaque chaîne spécifiée comme argument comme sous-chaîne. ga signifie grep all et gai ignore 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 "$@") -; }
7
nisetama

git grep

Voici la syntaxe utilisant git grep combinant plusieurs modèles à l'aide des expressions booléennes :

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

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

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

Vérifier man git-grep pour aider.

Voir également:

Pour le fonctionnement [~ # ~] ou [~ # ~] , voir:

4
kenorb

Voici mon point de vue, et cela fonctionne pour les mots sur plusieurs lignes:

Utilisation find . -type f suivi par autant
-exec grep -q 'first_Word' {} \;
et le dernier mot-clé avec
-exec grep -l 'nth_Word' {} \;

-q calme/silencieux
-l afficher les fichiers avec des correspondances

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

2
StackRover

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 grepping les plus rapides, car il est construit sur le moteur 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é associée sur GH-875 .

1
kenorb

Cette commande Shell peut aider:

eval "</dev/stdin $(printf "|grep '%s'" pattern1 pattern2)" FILE

Cependant, c'est plus facile si tous vos modèles sont stockés dans le fichier, vous pouvez donc définir l'alias suivant:

alias grep-all="cat $(xargs printf '| grep "%s"' < patterns.txt)"

et l'utiliser comme:

cat text.txt | grep-all

Vous pouvez bien sûr modifier l'alias en fonction de votre syntaxe requise, donc avec cet alias:

alias grep-all="</dev/stdin $(xargs printf '|grep "%s"' < patterns.txt)"

vous pouvez utiliser une seule commande, par exemple.

grep-all text.txt

Pour plus d'idées, vérifiez également: Faites correspondre tous les modèles du fichier à la fois .

Pour l'opération AND par fichier, voir: Vérifiez si toutes les chaînes ou expressions régulières multiples existent dans un fichier .

1
kenorb