Grep en quelques milliers de fichiers

13

J'ai un répertoire avec des fichiers cca 26 000 et j'ai besoin de grep dans tous ces fichiers. Le problème est que j'en ai besoin le plus rapidement possible, il n'est donc pas idéal de créer un script où grep prendra le nom d'un fichier de la commande find et écrira des correspondances dans le fichier. Avant le problème de la "liste des arguments trop longue", il a fallu environ 2 minutes au cca pour grep dans tous ces fichiers. Des idées comment le faire? edit: il y a un script qui crée tout le temps de nouveaux fichiers, il n'est donc pas possible de mettre tous les fichiers dans des répertoires différents.

user2778979
la source
1
utiliser findavec xargsougrep -R
Eddy_Em
Cela fonctionne bien, mais cela prend 10 minutes ...
user2778979

Réponses:

19

Avec find:

cd /the/dir
find . -type f -exec grep pattern {} +

( -type fconsiste à rechercher uniquement dans les fichiers normaux (excluant également les liens symboliques même s'ils pointent vers des fichiers normaux). Si vous souhaitez rechercher dans n'importe quel type de fichier à l'exception des répertoires (mais attention, il existe certains types de fichiers comme fifos ou / dev / zero qui vous ne voulez généralement pas lire), remplacez -type fpar le spécifique à GNU ! -xtype d( -xtype dcorrespond aux fichiers du répertoire type après la résolution du lien symbolique)).

Avec GNU grep:

grep -r pattern /the/dir

(mais attention, sauf si vous avez une version récente de GNU grep, cela suivra les liens symboliques lors de la descente dans les répertoires). Les fichiers non réguliers ne seront pas recherchés sauf si vous ajoutez une -D readoption. Cependant, les versions récentes de GNU grepne rechercheront toujours pas dans les liens symboliques.

Les très anciennes versions de GNU findne prenaient pas en charge la {} +syntaxe standard , mais là, vous pouviez utiliser la non standard:

cd /the/dir &&
  find . -type f -print0 | xargs -r0 grep pattern

Les performances sont susceptibles d'être liées aux E / S. C'est le temps de faire la recherche qui serait le temps nécessaire pour lire toutes ces données du stockage.

Si les données se trouvent sur une grappe de disques redondante, la lecture de plusieurs fichiers à la fois peut améliorer les performances (et les dégrader autrement). Si les performances ne sont pas liées aux E / S (car, par exemple, toutes les données sont dans le cache) et que vous disposez de plusieurs processeurs, l'utilisation simultanée grepspeut également être utile. Vous pouvez le faire avec xargsl' -Poption GNU .

Par exemple, si les données se trouvent sur une matrice RAID1 avec 3 disques, ou si les données sont dans le cache et que vous avez 3 processeurs dont vous avez le temps:

cd /the/dir &&
  find . -type f -print0 | xargs -n1000 -r0P3 grep pattern

(ici, utiliser -n1000pour générer un nouveau greptous les 1000 fichiers, jusqu'à 3 fonctionnant en parallèle à la fois).

Cependant, notez que si la sortie de grepest redirigée, vous vous retrouverez avec une sortie mal entrelacée des 3 grepprocessus, auquel cas vous voudrez peut-être l'exécuter comme:

find . -type f -print0 | stdbuf -oL xargs -n1000 -r0P3 grep pattern

(sur un système GNU ou FreeBSD récent) ou utilisez l' --line-bufferedoption GNU grep.

S'il patterns'agit d'une chaîne fixe, l'ajout de l' -Foption pourrait améliorer les choses.

Si ce ne sont pas des données de caractères multi-octets, ou si pour la correspondance de ce modèle, peu importe si les données sont des caractères multi-octets ou non, alors:

cd /the/dir &&
  LC_ALL=C grep -r pattern .

pourrait améliorer considérablement les performances.

Si vous finissez souvent par effectuer de telles recherches, vous souhaiterez peut-être indexer vos données à l'aide de l'un des nombreux moteurs de recherche disponibles.

Stéphane Chazelas
la source
3

26000 fichiers dans un seul répertoire, c'est beaucoup pour la plupart des systèmes de fichiers. Il est probable qu'une partie importante du temps soit consacrée à la lecture de ce gros répertoire. Pensez à le diviser en répertoires plus petits avec seulement quelques centaines de fichiers chacun.

L'appel findne peut expliquer les mauvaises performances que si vous le faites mal. C'est un moyen rapide de parcourir un répertoire et de vous assurer que vous ne risquez pas d'essayer d'exécuter une ligne de commande trop longue. Assurez-vous que vous utilisez -exec grep PATTERN {} +, qui contient autant de fichiers que possible par appel de commande, et non -exec grep PATTERN {} \;, qui s'exécute grepune fois par fichier: l'exécution de la commande une fois par fichier sera probablement beaucoup plus lente.

Gilles 'SO- arrête d'être méchant'
la source
Merci, je vais chercher quelque chose sur Google et je vais probablement le partager. J'ai fait exactement ce que vous écrivez et cela a pris 3 fois plus de temps que seulement grep ...
user2778979
Gilles, dites-vous que les performances différeraient considérablement pour 26 000 fichiers dans un même répertoire contre 26 000 fichiers répartis sur, disons, 100 répertoires?
user001
1
@ user001 Oui. Leur différence dépend du système de fichiers et éventuellement du stockage sous-jacent, mais je m'attends à ce que tout système de fichiers soit sensiblement plus rapide avec 260 fichiers dans chacun des 100 répertoires, contre 26000 fichiers dans un seul répertoire.
Gilles 'SO- arrête d'être méchant'
Merci pour la clarification. J'ai posé une question de suivi sur ce point afin de comprendre la base de l'écart.
user001
0

Si vous avez besoin de grep TOUS les fichiers plusieurs fois (comme vous l'avez dit, en exécutant un script), je suggérerais de regarder dans les disques RAM, d'y copier tous les fichiers puis de grep les fichiers plusieurs fois, cela accélérera votre recherche d'un facteur de au moins 100x.

Vous avez juste besoin de suffisamment de bélier. Sinon, vous devriez examiner l'indexation des fichiers, par exemple. dans lucene ou une base de données nosql, puis en exécutant des requêtes à ce sujet.

Tobias Feldballe
la source
Comme indiqué ailleurs, cela n'aide pas le fait qu'il y a trop de fichiers pour exécuter une grepcontre. Il y a aussi le fait que: "il y a un script qui crée tout le temps de nouveaux fichiers, donc il n'est pas possible de mettre tous les fichiers dans des répertoires différents."
Jeff Schaller
-2

Tous les fichiers du répertoire

grep 'search string' *

avec récursivement

grep -R 'search string' *
Markus
la source
Vous voulez élaborer le -1?
Markus
4
Je n'ai pas déçu, mais il y a quelques problèmes avec le vôtre: l'OP a mentionné une "liste d'arguments trop longue", que votre première ne résoudra pas et est probablement ce que l'OP faisait auparavant. Le second n'aide pas non plus à cet égard (aurait aidé si vous aviez utilisé à la .place de *). *exclura les fichiers de points (bien qu'avec -R, pas ceux des répertoires récursifs). -R par opposition à -r suit les liens symboliques même avec les versions récentes de GNU grep. Vous aurez également un problème avec les fichiers du répertoire actuel dont le nom commence par-
Stéphane Chazelas