web-dev-qa-db-fra.com

Fichiers Grep contenant deux occurrences ou plus d'une chaîne spécifique

J'ai besoin de trouver des fichiers où une chaîne spécifique apparaît deux fois ou plus.

Par exemple, pour trois fichiers:

Fichier 1: 

Hello World!

Fichier 2:

Hello World!
Hello !

Fichier 3:

Hello World!
Hello
Hello Again.

-

Je veux grep Hello et ne récupérer que les fichiers 2 & 3.

Et ça:

grep -o -c Hello * | awk -F: '{if ($2 > 1){print $1}}'
17
John C

Étant donné que la question est étiquetée grep, voici une solution utilisant uniquement cet utilitaire et bash (aucune awk requise):

#!/bin/bash
for file in *
do
  if [ "$(grep -c "Hello" "${file}")" -gt 1 ]
  then
    echo "${file}"
  fi
done

Peut être un one-liner:

for file in *; do if [ "$(grep -c "Hello" "${file}")" -gt 1 ]; then echo "${file}"; fi; done

Explication

  • Vous pouvez modifier l’instruction for file in * avec l’extension de shell souhaitée pour obtenir tous les fichiers de données.
  • grep -c renvoie le nombre de lignes correspondant au modèle, avec plusieurs correspondances sur une ligne comptant toujours pour une seule ligne correspondante.
  • if [ ... -gt 1 ] vérifie que plusieurs lignes correspondent dans le fichier. Si c'est le cas:
  • echo ${file} affiche le nom du fichier.
4
savanto

Après avoir lu votre question, je pense que vous souhaitez également rechercher l’affaire hello hello sur une ligne. (find files where a specific string appears twice or more.) alors je viens avec ce one-liner:

awk -v p="hello" 'FNR==1{x=0}{x+=gsub(p,p);if(x>1){print FILENAME;nextfile}}' *
  • dans la ligne ci-dessus, p est le motif que vous souhaitez rechercher
  • il imprimera le nom de fichier si le fichier contient le motif deux fois ou plus. peu importe qu'ils soient dans les mêmes lignes ou des lignes différentes
  • pendant le traitement, après avoir vérifié une ligne, si nous avions déjà trouvé deux modèles ou plus, imprimons le nom de fichier et arrêtons le traitement du fichier actuel, prenons le fichier d'entrée suivant, s'il en existe toujours. Ceci est utile si vous avez de gros fichiers.

Un petit test:

kent$  head f*
==> f <==
hello hello world

==> f2 <==
hello

==> f3 <==
hello
hello
SK-Arch 22:27:00 /tmp/test
kent$ awk -v p="hello" 'FNR==1{x=0}{x+=gsub(p,p);if(x>1){print FILENAME;nextfile}}' f*
f
f3
1
Kent

Cette awk imprimera le nom de tous les fichiers avec 2 ou plus Hello

awk 'FNR==1 {if (a>1) print f;a=0} /Hello/ {a++} {f=FILENAME} END {if (a>1) print f}' *
file2
file3
1
Jotne

Ce dont vous avez besoin est une variable grep capable de reconnaître les motifs entre les fins de ligne ( "bonjour" suivie de tout (éventuellement de fins de ligne), suivie de "bonjour" )

Étant donné que grep traite vos fichiers ligne par ligne, il n’est pas (en lui-même) le bon outil pour le travail - sauf si vous parvenez à regrouper l’ensemble du fichier en une seule ligne.

Maintenant, c'est facile, par exemple, en utilisant la commande tr, en remplaçant les fins de ligne par des espaces:

if cat $file | tr '\n' ' ' | grep -q 'hello.*hello'
then
   echo "$file matches"
fi

Ceci est très efficace, même sur des fichiers volumineux comportant beaucoup (par exemple 100 000) lignes, et peut être encore plus efficace en appelant grep avec --max-count=1, ce qui permet d’arrêter la recherche après la recherche d’une correspondance. Peu importe que les deux bonjours soient sur la même ligne ou non.

1
Hans Lub

grep -c Bonjour * | egrep -v ': [01] $' | sed 's /: [0-9] * $ //'

0
Chaim Geretz

Autrement:

grep Hello * | cut -d: -f1 | uniq -d

Grep pour les lignes contenant 'Hello'; ne garder que les noms de fichiers; n'imprimez que les doublons.

0
Pere