Comment utiliser grep sur tous les fichiers de manière non récursive dans un répertoire?

35

Je veux rechercher une chaîne de texte dans tous les fichiers d'un répertoire (et non dans ses sous-répertoires; je sais que l' -roption fait cela, mais ce n'est pas ce que je veux).

  1. Fonctionnement

    grep "string" /path/to/dir
    

    est censé être en mesure de le faire, j'ai lu, mais cela me donne l'erreur:

    grep: dir: est un répertoire

  2. Ensuite, j'ai essayé de courir grepsur plusieurs fichiers.

    grep "string" .bashrc .bash_aliases fonctionne parfaitement.

    grep "string" .bash* fonctionne comme prévu aussi.

    grep "string" * me donne les erreurs:

    grep: data: Is a directory
    grep: Desktop: Is a directory
    grep: Documents: Is a directory
    grep: Downloads: Is a directory
    ...
    

Seules les erreurs sont imprimées, je n'ai pas les lignes correspondantes. J'ai essayé d'utiliser l' -soption, mais en vain.

Alors, mes questions:

  1. Pourquoi ne puis-je pas utiliser grepsur un répertoire, comme dans (1), alors que je devrais pouvoir le faire? J'ai vu cela dans de nombreux exemples sur Internet.
    Edit : Quand je dis "utiliser grep sur un répertoire", je veux dire "rechercher dans tous les fichiers de ce répertoire à l'exception de ses sous-répertoires". Je crois que c'est ce que fait grep lorsque vous lui passez un répertoire à la place d'un fichier. Suis-je incorrect?

  2. Veuillez me donner une explication sur le fonctionnement de grepcela qui expliquerait le comportement des commandes dans (2).
    Edit : Permettez-moi d'être plus précis. Pourquoi utilise-t-on des caractères génériques pour spécifier plusieurs fichiers dans lesquels rechercher le travail .bash*et non avec *ou même ./*?

  3. Comment puis-je rechercher tous les fichiers d'un répertoire (et non ses sous-répertoires) en utilisant grep?

John Red
la source
Vous comptez également sur les caractères génériques d'extension du shell tels que *, appelés globbing. Globbing n'inclut pas les noms de fichiers commençant par un point tel .bashrcque standard. Vous pouvez définir les options du shell pour qu'il inclue ces fichiers, mais vous pouvez vous retrouver dans un désordre si vous ne savez pas ce que vous faites. Un bon guide pour comprendre la globalisation peut être trouvé ici mywiki.wooledge.org/glob
Arronical
Je ne sais pas pourquoi, mais j'ai toujours fait du globbing sur des fichiers cachés, et cela a toujours fonctionné. Je n'ai changé aucun paramètre ou quelque chose. Comme je l'ai souligné dans (2), cela fonctionne grep "string" .bash*aussi avec .
John Red
Désolé, mon dernier exemple était incorrect. Vous pouvez également rechercher dans des fichiers cachés et supprimer le "est un répertoire" car Linux considère techniquement les répertoires comme un type de fichier différent. Le commandement serait alors: grep "string" * .* 2>/dev/nullougrep -s "string" * .*
Terrance
Aussi ce stackoverflow.com/q/9217185/3701431
Sergiy Kolodyazhnyy

Réponses:

41

Dans Bash, un glob ne se développera pas en fichiers cachés, donc si vous voulez rechercher tous les fichiers dans un répertoire, vous devez spécifier les fichiers cachés .*et non cachés *.

Pour éviter les erreurs "Est un répertoire", vous pouvez utiliser -d skip, mais sur mon système, j'obtiens également une erreur grep: .gvfs: Permission denied , donc je suggère d'utiliser -s, qui masque tous les messages d'erreur.

La commande que vous recherchez est donc:

grep -s "string" * .*

Si vous recherchez des fichiers dans un autre répertoire:

grep -s "string" /path/to/dir/{*,.*}

Une autre option consiste à utiliser l' dotgloboption shell, qui fera qu'un glob inclura des fichiers cachés.

shopt -s dotglob
grep -s "string" *

Pour les fichiers dans un autre répertoire:

grep -s "string" /path/to/dir/*

† Quelqu'un a mentionné que je ne devrais pas recevoir cette erreur. Ils ont peut-être raison - j'ai fait un peu de lecture mais je n'ai pas pu en faire des têtes ou des queues moi-même.

wjandrea
la source
Y a-t-il une raison pour l'espace entre *et .*?
Hashim
2
@Hashim Comparez la sortie de echo * .*et echo *.*exécutez dans votre répertoire personnel, et la différence devrait être évidente. Sinon LMK et je vais l'expliquer.
wjandrea
Intéressant, echo *affiche donc les fichiers et dossiers echo *.*non masqués, echo .*affiche les fichiers non masqués, affiche tous les fichiers et echo * .*affiche tous les fichiers et répertoires. Mais pourquoi la raison de l'espace entre les deux dans ce dernier cas? Cela me semble désordonné. N'y a-t-il pas moyen de combiner les deux pour obtenir les mêmes résultats? Ou sinon existe-t-il une explication syntaxique de la raison pour laquelle les deux doivent être séparés ici, ou est-ce * .*un cas exceptionnel?
Hashim
1
@Hashim Je ne sais pas comment vous en êtes arrivé à ces conclusions, alors laissez-moi vous expliquer. Tout d'abord, les répertoires sont des fichiers dans ce contexte. Dans les globes, *représente tous les fichiers non cachés (c'est-à-dire les noms de fichiers qui ne commencent pas par un point); .*représente tous les fichiers cachés (c. -à- noms de fichiers qui ne commence par un point); et *.*représente tous les fichiers non cachés qui contiennent un point. Dans echo * .*, les deux globes doivent être séparés car ce sont des globes différents: un pour les non cachés, un pour les cachés. Bien que comme je l'ai écrit dans ma réponse, vous pouvez faire *inclure des fichiers cachés en activant l' dotgloboption shell.
wjandrea
1
L'utilisation *.*est courante sous Windows (DOS) pour répertorier tous les fichiers, mais sur * nix n'inclura que les fichiers contenant un point, donc cela n'a aucun sens sur * nix. Au lieu de cela, vous utilisez *pour répertorier tous les fichiers à l'exception des fichiers masqués et .*pour répertorier les fichiers masqués.
thomasrutter
11

Vous avez besoin de l' -d skipoption ajoutée.

  1. Grep recherche à l'intérieur des fichiers. Vous pouvez effectuer une recherche récursive, comme vous l'avez dit, si vous souhaitez rechercher des fichiers à l'intérieur d'un répertoire.

  2. Par défaut, grep lira tous les fichiers et détectera les répertoires. Parce que par défaut, vous n'avez pas défini quoi faire avec les répertoires avec l' -doption, cela donne une sortie d'erreur.

  3. Rechercher uniquement dans le répertoire parent serait grep -d skip "string" ./*

anonyme2
la source
Pour plus d'informations sur grep, voir man grep.
anonyme2
(a) Veuillez consulter l'édition. (b) L'utilisation -d skipne fonctionne pas; c'est fondamentalement la même chose que -s; voir aussi l'édition. (c) Non, grep -d skip "string" ./*ne fonctionne pas non plus.
John Red
7

Les anciens temporisateurs feraient probablement ceci:

find . -type f -print0 | xargs -0 grep "string"
Dathompson
la source
3
Pourquoi ne pas find . -type f -exec grep string {} +?
wchargin
5
Tu veux aussi -maxdepth 1.
wchargin
@wchargin: si vous voulez le nom du fichier dans la sortie alors qu'il n'y a qu'un seul fichier, je pense que vous voulezfind . -type f -maxdepth 1 -exec grep string /dev/null {} +
Gregory Nisbet
1
@GregoryNisbet: Passez simplement -Hà grep.
wchargin
2

Reformulation - vous voulez grep les fichiers dans un seul niveau de sous-répertoire, mais pas récursif dans tous les sous-sous-répertoires?

grep forthis  *  */*

Ou si vous ne voulez pas les fichiers dans le répertoire courant

grep forthis  */*

Notez que cela ne trouvera pas les répertoires commençant par un point.

grep forthis  .*/*    */*   

devrait faire ce travail.

Il existe également des paramètres de restriction -maxdepthet -mindepthdisponibles pour la findcommande.

Criggie
la source
Ne grep forthis */*rechercherait -il pas des fichiers à la fois dans le répertoire actuel et dans un répertoire vers le bas?
Hashim
@Hashim nope principalement - cos */*ne fait correspondre les choses qu'avec une seule barre oblique. Si vous aviez un fichier nommé a/bdans le répertoire courant, alors `* / * correspondrait à cela.
Criggie
0

Voici un exemple pour ignorer les répertoires sans ignorer toutes les erreurs:

grep --directories='skip' 'searchString' *
Mojtaba Rezaeian
la source