Comment exclure certains fichiers ne correspondant pas à certaines extensions avec grep?

8

Je veux sortir toutes les lignes contenant le mot OKrécursivement à partir d'un répertoire. Mais il y a quelques extensions que je dois exclure du résultat:

*~
*.map
*.js except *.debug.js

J'ai essayé:

grep -r --exclude={*~,*.map} "OK" /some/dir

Sauf que je ne sais pas comment supprimer du résultat tous ces .jsfichiers non débogués .

Débordement de question
la source

Réponses:

7

Je voudrais simplement passer cela une seconde greppour les supprimer:

grep -r --exclude={\*~,\*.map} "OK" bar/ | grep -vP '(?<!debug)\.js'

L' -vinverse la correspondance, l'impression de lignes qui ne correspondent pas au motif et -Ppermet les expressions régulières compatibles Perl qui nous permettent d'utiliser des lookbhinds négatifs . Cette expression régulière particulière correspondra à ce .jsqui n'est pas précédé par debugquel moyen (puisque nous inversons les correspondances) que seuls ces .jsfichiers seront imprimés.

Cependant, comme l'a souligné @QuestionOverflow dans les commentaires, cela pourrait avoir pour effet secondaire inattendu de filtrer les lignes qui contiennent OKet jspuisque le grep -vest appliqué à la sortie entière, pas seulement le nom du fichier. Pour éviter cela, ajoutez simplement deux points (c'est ce qui permet grepde séparer les noms de fichier du contenu du fichier):

grep -r --exclude={*~,*.map} "OK" bar/ | grep -vP '(?<!debug).js:'

Cela échouera toujours si votre ligne d'entrée contient foo.js:ou si votre nom de fichier contient :. Donc, pour être sûr, utilisez une approche différente:

grep -Tr --exclude={*~,*.map} "OK" bar/ | grep -vP '(?<!debug).js\t'

Les -Tcauses greppour imprimer un onglet entre le nom de fichier et le contenu du fichier. Donc, si nous ajoutons simplement un \tà la fin de l'expression régulière, il ne correspondra qu'aux noms de fichiers et non au contenu de la ligne.

Néanmoins, l' utilisationfind pourrait avoir plus de sens malgré tout.

terdon
la source
1
Serais-je exclu par inadvertance des lignes dans les fichiers que je veux, mais contenant les deux OKet .jssur la même ligne?
Débordement de questions
@QuestionOverflow ah, oui en effet, bonne prise. Voir la réponse mise à jour.
terdon
Réponse fantastique. Je dois accepter le vôtre puisque je demande spécifiquement grep. Merci.
Débordement de questions
@QuestionOverflow vous êtes les bienvenus. En général cependant, findc'est probablement mieux pour ce genre de chose. Obtenir le bon greppeut être délicat comme vous l'avez souligné :).
terdon
Vos solutions échouent si l'une des failgloboptions est définie dans le shell: bash: no match: --exclude=*~ vous devez citer vos arguments de modèle GLOB --excludepour les cacher de l'expansion du shell, par exemple--exclude={\*~,\*.map}
Ian D. Allen
7

J'utiliserais findpour localiser les fichiers et diriger le résultat via xargs:

$ find . -type f \! -name "*~" \
                 \! -name "*.map" \
                 \! \( -name "*.js" -and \! -name "*.debug.js" \) \
         -print0 | xargs -0 grep "OK"

Ceci recherche chaque fichier ne correspondant pas à " *~", " *.map" ou " *.jsmais pas *.debug.js".

En utilisant findvous pouvez facilement rechercher des règles plutôt complexes et cette approche vous évite de supprimer accidentellement les faux positifs comme cela pourrait arriver avec le double grep.

Andreas Wiese
la source
Belle réponse aussi :)
Débordement de la question
3
Oui, c'est probablement le meilleur moyen, +1. Vous pouvez également utiliser -exec grep OK {} +au lieu de xargset éviter un programme supplémentaire.
terdon
2
@IDAllen non, notez que j'ai suggéré que -exec +non -exec \;, cela exécutera le moins de commandes possible, un peu comme xargs.
terdon
4

Avec zshvous pouvez faire:

setopt extendedglob
grep OK some/dir/**/^(*~|*.map|(^*debug).js)

À condition bien sûr que la liste des arguments ne soit pas trop longue, auquel cas vous pouvez toujours faire:

printf '%s\0' some/dir/**/^(*~|*.map|(^*debug).js) | xargs -0 grep OK
Graeme
la source
En outre, vous pouvez faire le dernierzshautoload zargszargs some/dir/**/^(*~|*.map|(^*debug).js) -- grep OK
uniquement
2

Si cela ne vous dérange pas de voir la sortie légèrement hors service (si vous le faites, vous pouvez la trier):

grep -r --exclude={*~,*.map,*.js} "OK" /some/dir **/*.debug.js

Cela nécessite que votre shell prenne en charge la globalisation **récursive: zsh est shopt -s globstarprêt à l'emploi , bash le fait après l'exécution , ksh93 le fait après l'exécution set -o globstar.

Sans **prise en charge dans le shell, vous pouvez utiliser deux commandes grep:

grep -r --exclude={*~,*.map,*.js} "OK" /some/dir
grep -r --include=*.debug.js "OK" /some/dir
Gilles 'SO- arrête d'être méchant'
la source
Mon shell prend en charge **, mais il semble y avoir quelque chose de mal avec l'argument supplémentaire **/*.debug.js, provoquant l'interprétation de grep OKcomme un répertoire. Avez-vous essayé de l'exécuter?
Débordement de questions
@QuestionOverflow Mon erreur, j'avais inversé l'ordre des arguments.
Gilles 'SO- arrête d'être méchant'
2

Vous pouvez utiliser ripgrep. Par défaut, il ignore les fichiers cachés et respecte votre .gitignorefichier.

Vous pouvez spécifier les règles d'inclusion ou d'exclusion à l'aide des paramètres suivants:

-g/ --glob GLOBInclure ou exclure des fichiers et des répertoires pour la recherche qui correspondent au glob donné.

-t/ --type TYPERechercher uniquement les fichiers correspondant à TYPE. Plusieurs indicateurs de type peuvent être fournis.

-T/ --type-not TYPENe recherchez pas les fichiers correspondant à TYPE.

Utilisez l' --type-listindicateur pour répertorier tous les types disponibles.

Voici quelques exemples simples:

rg -Tjs "OK"                              # Excludes *.js, *.jsx, *.vue files.
rg -tpy "OK"                              # Includes Python files.
rg --type-add 'map:*.map' -tmap PATTERN   # Excludes *.map files.
rg -g '!*.js' -g '*.debug.js' PATTERN     # Excludes *.js apart of *.debug.js.

Voici la solution complète pour exclure *.~, *.map, *.jsmais non *.debug.js:

rg -g '*.*' -g '!*.~' -g '!*.map' -g '!*.js' -g '*.debug.js' "OK"

Essai:

$ touch file.~ file.map file.js file.debug.js file.txt file.md
$ rg --files
file.debug.js
file.js
file.map
file.md
file.txt
$ rg -g '*.*' -g '!*.~' -g '!*.map' -g '!*.js' -g '*.debug.js' --files
file.debug.js
file.md
file.txt
kenorb
la source