Grep récursif pour les mots dans un type de file particulier

Je voulais une command en command line pour searchr tous les scripts shell dans le système de files pour un mot particulier, alors j'ai demandé autour de travail et a obtenu les solutions suivantes:

grep word `find / -name \*.sh 2>/dev/null` find / -name "*.sh" 2>/dev/null | xargs grep word 

Cependant, je ne suis pas familier avec la command line, donc ces deux solutions me semblent opaques. Je préférerais faire quelque chose qui ressemble à ceci:

 ls -r *.sh | cat | grep -H word 

Mais il semble que vous ne pouvez pas canaliser les noms de files dans le chat (au less, je pense que c'est ce que le problème est).

Quelle est la solution la plus lisible? Et deuxièmement, quelle est la solution la plus efficace?

Edit: J'ai besoin de savoir dans quel file le mot a été trouvé, afin de pouvoir modifier le script.

Edit: Si vous avez des utilitaires GNU, voir la réponse de Gilles pour une méthode utilisant les capacités de recursion de GNU grep qui est beaucoup plus simple que l'approche find . Si vous voulez seulement afficher les noms de files, vous voudrez toujours append l'option -l comme décrit ci-dessous.


Utilisez grep -l word pour imprimer uniquement les noms des files contenant une correspondance.

Si vous voulez find tous les files du système de files se terminant par .sh , à partir de la racine / , find est l'outil le plus approprié.

La recommandation la plus portable et la plus efficace est la suivante:

 find / -type f -name '*.sh' -exec grep -l word {} + 2>/dev/null 

C'est à peu près aussi lisible que possible et n'est pas difficile à parsingr si vous comprenez la sémantique derrière chacun des composants.

  • find / : run find partir de la racine du système de files, /
  • -type f : ne correspond qu'aux files réguliers
  • -name '*.sh' : … et ne correspondent qu'aux files dont les noms se terminent par .sh
  • -exec ... {} + : exécutez la command spécifiée dans ... sur les files correspondants dans les groupes, où {} est remplacé par les noms de file dans le groupe. L'idée est d'exécuter la command sur autant de files à la fois que possible dans les limites du système ( ARG_MAX ). L'efficacité de la forme {} + provient de la minimisation du nombre de fois que la command ... doit être appelée en maximisant le nombre de files transmis à chaque invocation de ...
  • grep -l word {} : où le {} est le même {} répété d'en haut et est remplacé par des noms de file. Comme expliqué précédemment, grep -l imprime les noms des files contenant une correspondance pour le word .
  • 2>/dev/null : cacher les messages d'erreur (techniquement, redirect l'erreur standard vers le trou noir qui est /dev/null ). C'est pour des raisons esthétiques et pratiques, car la find cours sur / entraînera vraisemblablement des rames de messages "permission denied" qui ne vous intéressent pas pour les files que vous n'avez pas la permission de lire et les directorys que vous n'avez pas la permission de parcourir.

Il y a quelques problèmes avec les suggestions que vous avez reçues et affichées dans votre question. Tous les deux

 grep word `find / -name \*.sh 2>/dev/null 

et

 find / -name "*.sh" 2>/dev/null | xargs grep word 

échouer sur les files avec des espaces dans leur nom. Il est préférable d'éviter de placer les noms de files dans la substitution de command. Le premier a le problème supplémentaire de potentiellement courir dans la limite ARG_MAX. Le second est proche de ce que je suggère, mais il n'y a pas de bonne raison d'utiliser xargs ici, sans mentionner que l'utilisation sûre et correcte de xargs nécessite de sacrifier la portabilité pour certaines options GNU-only ( find -print0 | xargs -0 ).

Sur Linux non embarqué, Cygwin ou autre système avec GNU grep , sous FreeBSD , sous NetBSD et OSX :

 grep -r --include='*.sh' word . 

Ne pas parsingr la sortie de ls . Et n'utilisez pas de substitution de command sur la sortie de find , comme l' a expliqué jw013 .

La combinaison de grep et find est dans de nombreux cas ack ( betterthangrep.com ):

 ack [OPTION]... PATTERN [FILE] 

Pour votre exemple, envisagez d'utiliser

 ack --shell word / 

Remarques

ack

  • searchs (par défaut) récursivement, mais
  • ignore (par défaut) les directorys des systèmes de contrôle de version courants, par exemple .git , .hg , .svn , …
  • peut facilement affiner vos résultats en utilisant des filters pour les types de files communs (voir ci-dessous pour les templates de noms de files distincts)
  • a une syntaxe de type grep et les mêmes arguments / similaires comme -i pour "ignorer la casse" etc.
  • peut être appelé ack-grep sur votre système (sur les dissortingbutions basées sur Debian, si je me souviens bien)

Modèles de noms de files

L'option --shell est courte pour --type=shell et inclut plusieurs types de files: actuellement .sh .bash .csh .tcsh .ksh .zsh selon

 ack --help-types 

Si vous ne voulez que des files .sh , vous devez définir (append) votre propre type sh et utiliser ce filter ( --sh ) comme ceci:

 ack word --type-add=sh=.sh --sh / 

Cela semble un peu compliqué, mais permet la search récursive pour les files .sh ci / dessous / . Pour une search locale (sans spécifier le directory de départ, par exemple \ ), ce serait plus simple:

 ack word *.sh