J'essaie d'utiliser grep
pour faire correspondre les lignes contenant deux chaînes différentes. J'ai essayé ce qui suit, mais cela correspond aux lignes qui contiennent soit string1 o string2 ce qui n'est pas ce que je veux.
grep 'string1\|string2' filename
Alors, comment faire correspondre avec grep
uniquement les lignes contenant les deux chaînes ?
Vous pouvez utiliser grep 'string1' filename | grep 'string2'
Ou, grep 'string1.*string2\|string2.*string1' filename
Je pense que c'est ce que vous cherchiez:
grep -E "string1|string2" filename
Je pense que cela répond comme ceci:
grep 'string1.*string2\|string2.*string1' filename
ne correspond que dans le cas où les deux sont présents, pas l'un ou l'autre ou les deux.
Donnez-lui simplement plusieurs options -e
.
-e pattern, --regexp=pattern
Specify a pattern used during the search of the input: an input
line is selected if it matches any of the specified patterns.
This option is most useful when multiple -e options are used to
specify multiple patterns, or when a pattern begins with a dash
(`-').
Ainsi, la commande devient:
grep -e "string1" -e "string2" filename
Remarque: J'ai cité ci-dessus le manuel de la version BSD, mais il semble que ce soit identique sous Linux .
Pour rechercher des fichiers contenant tous les mots dans n’importe quel ordre:
grep -ril \'action\' | xargs grep -il \'model\' | xargs grep -il \'view_type\'
Le premier grep lance une recherche récursive (r
) en ignorant la casse (i
) et en listant (en imprimant) le nom des fichiers qui correspondent (l
) à un terme ( 'action'
avec les guillemets simples) survenant n'importe où dans le fichier.
Les greps suivants recherchent les autres termes, conservent l’insensibilité à la casse et répertorient les fichiers correspondants.
La liste finale des fichiers que vous obtiendrez sera ceux qui contiennent ces termes, dans n'importe quel ordre, dans le fichier.
Si vous avez une grep
avec une option -P
pour une regex Perl
limitée, vous pouvez utiliser
grep -P '(?=.*string1)(?=.*string2)'
qui a l'avantage de travailler avec des chaînes qui se chevauchent. C'est un peu plus simple d'utiliser Perl
comme grep
, car vous pouvez spécifier plus directement la logique et:
Perl -ne 'print if /string1/ && /string2/'
Votre méthode était presque bonne, il ne manque que le -w
grep -w 'string1\|string2' filename
Vous pouvez essayer quelque chose comme ça:
(pattern1.*pattern2|pattern2.*pattern1)
L'opérateur |
dans une expression régulière signifie ou. C'est-à-dire que string1 ou string2 correspondront. Vous pourriez faire:
grep 'string1' filename | grep 'string2'
qui dirigera les résultats de la première commande vers le second grep. Cela devrait vous donner que des lignes qui correspondent à la fois.
Et comme les gens suggéraient Perl et python et les scripts Shell compliqués, voici une approche simple awk:
awk '/string1/ && /string2/' filename
Après avoir regardé les commentaires à la réponse acceptée: non, cela ne fait pas multi-ligne; mais ce n'est pas non plus ce que l'auteur de la question a demandé.
Lignes trouvées commençant seulement par 6 espaces et finies par:
cat my_file.txt | grep
-e '^ .*(\.c$|\.cpp$|\.h$|\.log$|\.out$)' # .c or .cpp or .h or .log or .out
-e '^ .*[0-9]\{5,9\}$' # numers between 5 and 9 digist
> nolog.txt
N'essayez pas d'utiliser grep pour cela, utilisez plutôt awk. Pour faire correspondre deux expressions rationnelles R1 et R2 dans grep, on pourrait penser que ce serait:
grep 'R1.*R2|R2.*R1'
en awk ce serait:
awk '/R1/ && /R2/'
mais que se passe-t-il si R2
chevauche ou est un sous-ensemble de R1
? Cette commande grep ne fonctionnerait tout simplement pas alors que la commande awk le ferait. Disons que vous voulez rechercher des lignes contenant the
et heat
:
$ echo 'theatre' | grep 'the.*heat|heat.*the'
$ echo 'theatre' | awk '/the/ && /heat/'
theatre
Vous devriez utiliser 2 greps et un tuyau pour cela:
$ echo 'theatre' | grep 'the' | grep 'heat'
theatre
et bien sûr, si vous aviez réellement exigé qu'ils soient séparés, vous pouvez toujours écrire dans awk la même expression rationnelle que celle utilisée dans grep. Il existe des solutions alternatives à awk qui n'impliquent pas de répéter les expressions rationnelles dans toutes les séquences possibles.
En mettant cela de côté, si vous vouliez étendre votre solution aux 3 expressions rationnelles R1, R2 et R3. En grep, ce serait un de ces mauvais choix:
grep 'R1.*R2.*R3|R1.*R3.*R2|R2.*R1.*R3|R2.*R3.*R1|R3.*R1.*R2|R3.*R2.*R1' file
grep R1 file | grep R2 | grep R3
dans awk ce serait le concis, évident, simple, efficace:
awk '/R1/ && /R2/ && /R3/'
Maintenant, si vous vouliez réellement faire correspondre les chaînes littérales S1 et S2 au lieu des expressions rationnelles R1 et R2? Vous ne pouvez simplement pas faire cela dans un appel à grep, vous devez soit écrire du code pour échapper à tous les métachars RE avant d'appeler grep:
S1=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<< 'R1')
S2=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<< 'R2')
grep 'S1.*S2|S2.*S1'
ou encore utilisez 2 greps et un pipe:
grep -F 'S1' file | grep -F 'S2'
encore une fois, ce sont de mauvais choix alors qu'avec awk, vous utilisez simplement un opérateur de chaîne de caractères au lieu d'un opérateur de regexp
awk 'index($0,S1) && index($0.S2)'
Maintenant, si vous vouliez faire correspondre deux expressions rationnelles dans un paragraphe plutôt que dans une ligne? Ne peut être fait dans grep, trivial dans awk:
awk -v RS='' '/R1/ && /R2/'
Que diriez-vous de tout un fichier? Encore une fois, cela ne peut pas être fait dans grep et trivial dans awk (cette fois, j’utilise GNU awk pour une RS à caractères multiples, mais ce n’est pas beaucoup plus de code dans n'importe quelle awk ou vous pouvez choisir un contrôle- vous ne saurez pas que le RS fera la même chose):
awk -v RS='^$' '/R1/ && /R2/'
Donc, si vous voulez trouver plusieurs expressions rationnelles ou chaînes dans une ligne, un paragraphe ou un fichier, n'utilisez pas grep, utilisez awk.
Disons que nous devons trouver le nombre de mots multiples dans un fichier de test de fichier. Il y a deux façons de s'y prendre
1) Utiliser la commande grep avec le motif correspondant à l'expression rationnelle
grep -c '\<\(DOG\|CAT\)\>' testfile
2) Utiliser la commande egrep
egrep -c 'DOG|CAT' testfile
Avec egrep, vous n'avez pas à vous soucier de l'expression, mais séparez les mots à l'aide d'un séparateur de tuyau.
grep ‘string1\|string2’ FILENAME
GNU grep version 3.1
git grep
Voici la syntaxe utilisant git grep
avec plusieurs modèles:
_git grep --all-match --no-index -l -e string1 -e string2 -e string3 file
_
Vous pouvez également combiner des motifs avec des expressions booléennes telles que _--and
_, _--or
_ et _--not
_.
Consultez _man git-grep
_ pour obtenir de l'aide.
_
--all-match
_ Lorsque vous spécifiez plusieurs expressions de modèle, cet indicateur est spécifié pour limiter la correspondance aux fichiers dont les lignes correspondent à toutes ._
--no-index
_ Recherche dans le répertoire actuel les fichiers qui ne sont pas gérés par Git._
-l
_/_--files-with-matches
_/_--name-only
_ Affiche uniquement les noms de fichiers._
-e
_ Le paramètre suivant est le motif. La valeur par défaut consiste à utiliser une expression rationnelle de base.
Autres paramètres à prendre en compte:
_
--threads
_ Nombre de threads de travail grep à utiliser._
-q
_/_--quiet
_/_--silent
_ Ne pas afficher les lignes correspondantes; quitter avec le statut 0 quand il y a une correspondance.
Pour changer le type de motif, vous pouvez également utiliser _-G
_/_--basic-regexp
_ (par défaut), _-F
_/_--fixed-strings
_, _-E
_/_--extended-regexp
_, _-P
_/_--Perl-regexp
_, _-f file
_ et autres.
Apparenté, relié, connexe:
Pour l'opération OU, voir:
grep -i -w 'string1\|string2' filename
Cela fonctionne pour la correspondance exacte de mots et la correspondance de mots insensibles à la casse, car -i est utilisé
Placez les chaînes que vous voulez grep dans un fichier
echo who > find.txt
echo Roger >> find.txt
echo [44][0-9]{9,} >> find.txt
Puis recherchez en utilisant -f
grep -f find.txt BIG_FILE_TO_SEARCH.txt
grep '(string1.*string2 | string2.*string1)' filename
obtiendra la ligne avec string1 et string2 dans n'importe quel ordre
pour une correspondance multiligne:
echo -e "test1\ntest2\ntest3" |tr -d '\n' |grep "test1.*test3"
ou
echo -e "test1\ntest5\ntest3" >tst.txt
cat tst.txt |tr -d '\n' |grep "test1.*test3\|test3.*test1"
nous avons juste besoin de supprimer le caractère de nouvelle ligne et cela fonctionne!
Je rencontre souvent le même problème que le vôtre et je viens d'écrire un morceau de script:
function m() { # m means 'multi pattern grep'
function _usage() {
echo "usage: COMMAND [-inH] -p<pattern1> -p<pattern2> <filename>"
echo "-i : ignore case"
echo "-n : show line number"
echo "-H : show filename"
echo "-h : show header"
echo "-p : specify pattern"
}
declare -a patterns
# it is important to declare OPTIND as local
local ignorecase_flag filename linum header_flag colon result OPTIND
while getopts "iHhnp:" opt; do
case $opt in
i)
ignorecase_flag=true ;;
H)
filename="FILENAME," ;;
n)
linum="NR," ;;
p)
patterns+=( "$OPTARG" ) ;;
h)
header_flag=true ;;
\?)
_usage
return ;;
esac
done
if [[ -n $filename || -n $linum ]]; then
colon="\":\","
fi
shift $(( $OPTIND - 1 ))
if [[ $ignorecase_flag == true ]]; then
for s in "${patterns[@]}"; do
result+=" && s~/${s,,}/"
done
result=${result# && }
result="{s=tolower(\$0)} $result"
else
for s in "${patterns[@]}"; do
result="$result && /$s/"
done
result=${result# && }
fi
result+=" { print "$filename$linum$colon"\$0 }"
if [[ ! -t 0 ]]; then # pipe case
cat - | awk "${result}"
else
for f in "$@"; do
[[ $header_flag == true ]] && echo "########## $f ##########"
awk "${result}" $f
done
fi
}
Usage:
echo "a b c" | m -p A
echo "a b c" | m -i -p A # a b c
Vous pouvez le mettre en .bashrc si vous voulez.
Vous devriez avoir grep
comme ceci:
$ grep 'string1' file | grep 'string2'