Rechercher uniquement dans les fichiers correspondant à un modèle avec ack


50

Peut effectuer une recherche uniquement dans les fichiers correspondant à un modèle 'glob' spécifique (par exemple, recherchez foo dans tous les fichiers nommés "bar * .c"). La commande

ack foo "bar*.c"

ne fonctionne que dans le répertoire en cours.

Note: Je sais que c'est possible avec find -exec:

find . -name "bar*.c" -type f -exec ack foo {} + 

Mais je voudrais une petite et simple commande ack, car find ne saute pas les répertoires de contrôle de version.


2
Je ne comprends pas, quel est le problème avec l'utilisation find . -name "bar*.c" -exec ack foo {} \;? Il n'y a rien de spécial grep, vous pouvez utiliser n'importe quelle commande avec find -exec.
terdon

1
@terdon trouve également des recherches dans les répertoires de contrôle de version et je ne le souhaite pas.
compie

2
Ensuite, éditez votre question et expliquez les limitations à contourner.
terdon

Réponses:


29

Recherche dans les annuaires

D'après le synopsis présenté dans la page de manuel, je dirais que oui, il peut traiter un répertoire, mais en regardant les commutateurs, il ne peut pas rechercher uniquement un fichier basé sur un modèle. Pour cela, vous devrez vous enrôler find. La commande ackinclut l'option --files-from=FILEafin qu'elle puisse être alimentée avec une liste de fichiers find.

synopsis

       ack [options] PATTERN [FILE...]
       ack -f [options] [DIRECTORY...]

usage

   --files-from=FILE
       The list of files to be searched is specified in FILE.  The list of
       files are separated by newlines.  If FILE is "-", the list is
       loaded from standard input.

Il y a l' --ignore-file=option qui peut vous donner ce que vous voulez mais semble un peu pénible à utiliser.

   --ignore-file=FILTERTYPE:FILTERARGS
       Ignore files matching FILTERTYPE:FILTERARGS.  The filters are
       specified identically to file type filters as seen in "Defining
       your own types".

Recherche de types de fichiers spécifiques

La seule autre façon que je puisse concevoir de faire cela via ackconsiste à utiliser son --typecommutateur:

   --type=[no]TYPE
       Specify the types of files to include or exclude from a search.
       TYPE is a filetype, like perl or xml.  --type=perl can also be
       specified as --perl, and --type=noperl can be done as --noperl.

       If a file is of both type "foo" and "bar", specifying --foo and
       --nobar will exclude the file, because an exclusion takes
       precedence over an inclusion.

Pour voir quels types sont disponibles:

$ ack --help-types | grep -E "perl|cpp"

format. Par exemple, les deux types --type = perl et --perl fonctionnent. - [no] cpp .cpp .cc .cxx .m .hpp .hh .h .hxx - [no] objcpp .mm .h - [no] perl .pl .pm .pod .t .psgi; correspond à la première ligne /^#!.*\bperl/ - [no] perltest .t

Exemples

Trouvez tous les fichiers Perl, en fonction du nom de fichier (* .pm, * .pl, * .t et * .pod) et de la ligne shebang.

$ ack -f --type perl 
examples/iwatch/iwatch/iwatch
examples/nytprof_perl/bad.pl

Trouvez tous les fichiers C ++:

$ ack -f --type=cpp
Shared/Scanner.h
Shared/Sorter.h
Shared/DicomHelper.cpp
Shared/DicomDeviationWriter.h

Recherche de foo in bar * .c

Alors, comment pouvez-vous accomplir ce que vous voulez? Eh bien, vous devrez probablement utiliser findpour faire ceci:

$ find adir -iname "bar*.c" | ack --files-from=- foo
adir/bar1.c
1:foo
2:foo

adir/dir1/bar1.c
1:foo
2:foo

Vous pouvez également utiliser ackla possibilité de rechercher des fichiers qui correspondent à un modèle donné dans leurs noms de fichier (using -g <pattern>), puis de passer cette liste à un second appel de ackusing -xou --files-from=-..

Utilisant -x:

$ ack -g '\bbar.*.c$' | ack -x foo
bar1.c
1:foo
2:foo

dir1/bar1.c
1:foo
2:foo

Utilisant -files-from=-:

$ ack -g '\bbar.*.c$' | ack --files-from=- foo
bar1.c
1:foo
2:foo

dir1/bar1.c
1:foo
2:foo

Dans les deux cas, nous faisons correspondre les noms de fichiers que vous souhaitez utiliser avec cette expression rationnelle:

\bbar.*.c$

Cela correspond à des fichiers dont le nom est bar.*cet de fin après l' .cutilisation de la fin de l' ancrage de la ligne, $. Nous vérifions également que les noms ont un caractère de limite \b. Cela échouera pour les fichiers contenant des caractères de limite tels que $bar.cou %bar.cpar exemple.


1
Cela ne répond pas à ma question (recherche de foo dans tous les fichiers nommés "bar * .c")
compie

1
@compie - cela se produit dans le sens où je vous montre comment obtenir une tranche des fichiers que vous souhaitez. Pour rechercher des cppfichiers ack --type=cpp <string>. Voir ackla page de manuel de pour plus sur tout cela cependant. Mais ce que je vous dis fondamentalement, c'est que vous ne pouvez pas utiliser ackla méthode que vous souhaitez.
slm

1
il répond donc dans le sens où il montre que ackne peut pas faire ce qui a été demandé.
xealits

@xealits - cette partie y répond: Recherche de foo dans la barre * .c find adir -iname "bar*.c" | ack --files-from=- foo . Ou, si vous voulez juste un fichier:echo "barBaz.c" | ack --files-from=- foo
alexanderbird

2
tldr; ack -g '\bbar.*.c$' | ack -x foo
chim

14

C'est facile si le type de fichier est connu et ack connaît beaucoup de types de fichiers. Donc, si, par exemple, vous voulez chercher uniquement dans les fichiers C, vous pouvez le faire:

ack --cc 'string'

Mais si ce n'est pas une des extensions connues, vous devez définir votre propre type. Cela devrait fonctionner:

ack --type-set barc:match:/bar.+\.c/ --barc 'string'

Notez que vous avez besoin des deux types --type-set et --barc.

(Merci à Andy, qui a également contribué à cela sur la liste de diffusion.)


1
Ok ça marche mais utiliser find et 'ack -x' est plus simple. Je pense que je vais rester avec ça.
compie

Ajouter un lien vers la documentation ici serait utile. man ackn'aide pas vraiment et n'a pas ccquand je cherche ça.
YPCrumble

ack --help = types Il y a tout ce à quoi je peux penser. Je l'utilise principalement pour Python et Ada.
David Boshton

6

"Quoi de neuf dans ack 2?" http://beyondgrep.com/ack-2.0/

avec ack 2.0, vous pouvez utiliser le nouveau -x pour diriger les noms de fichiers d'un appel de ack à un autre.

ack2 -g -i filepattern | ack2 -x -w searchpattern

Seulement je ne peux pas le faire fonctionner:

% ack -g "*bar*.c"
Invalid regex '*bar*.c':
Quantifier follows nothing in regex; marked by <-- HERE in m/* <-- HERE bar*.c/ at ack line 314.

Ainsi, il semble que -g ait besoin d'une expression régulière, alors que je veux une option de style 'glob' ...


2
Oui, -gprend une regex, pas un glob. Vous pouvez écrire votre regex comme .*bar.*\.c$.
Andy Lester

2
@AndyLester Merci d'avoir confirmé. Aimez-vous l’idée d’une option «glob» pour ack?
compie

4

Cela semblerait être le plus rapide et le plus sûr:

find . -name '*.c' | ack -x 'some string'

-x Lire la liste des fichiers à rechercher à partir de STDIN.

Toutefois, si le fichier est susceptible de figurer dans votre locatebase de données, cela serait encore plus rapide:

locate --basename '*.c' | ack -x 'some thing'

Locate peut également accepter les expressions régulières old-school, un peu pénibles, mais si vous recherchez des cfichiers, cela peut être nécessaire. par exemple

locate --basename --regexp '\.\(c\|cpp\|h\|hpp\|cc\)$' |
    ack -x 'some thing'

Cela se traduit simplement par "recherche dans tous les noms de fichiers se terminant par c, cpp, h, hpp ou cc".


2

Ack ne prend pas en charge la sélection de fichiers de style glob. Depuis que cela me manque vraiment, j'ai créé un petit script shell ackg :

#!/bin/sh
# 'glob' support for ack
find -name "$2" -type f -exec ack "$1" {} +

Maintenant, vous pouvez utiliser la commande:

ackg foo "bar*.c"

Mais attention: ceci cherchera malheureusement aussi dans les répertoires de contrôle de version (par exemple: .git).


2

Cela peut être fait avec l' -Goption to ag, le chercheur d'argent (un clone amélioré de ack-grep).

$ echo foo > bar_wanted.c
$ echo foo > unwanted.c
$ ag -G "bar.*\.c" foo
bar_wanted.c
1:foo

Remarques:

  • agutilise la syntaxe d'expression régulière PCRE , donc l'expression rationnelle doit être à la bar.*\.cplace de bar*.c.
  • L' -Goption doit précéder le terme recherché, car tout ce qui suit est interprété comme un nom de fichier.

1

Voulez-vous seulement un certain motif ou voulez-vous simplement des fichiers source C?

Si vous voulez tous les fichiers C, utilisez

ack --cc foo

Si vous voulez que tous les fichiers C et les fichiers d’en-tête NOT C, utilisez

ack --cc --no-hh foo

1

La réponse de @chim est la meilleure, mais elle est écrite sous forme de commentaire, alors je la republie sous forme de réponse officielle ...

ack -g '\bbar.*.c$' | ack -x foo

J'aime le fait que tu puisses regex le chemin ...


En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.