Buscar solo en archivos que coincidan con un patrón con ack

49

Puede hacer búsquedas solo a través de archivos que coinciden con un patrón específico 'glob' (por ejemplo: buscar foo en todos los archivos llamados "bar * .c"). El comando

ack foo "bar*.c"

solo funciona en el directorio actual.

Nota: Sé que es posible con find -exec:

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

Pero me gustaría un comando ack pequeño y simple, porque find no omite los directorios de control de versiones.

compie
fuente
2
No entiendo, ¿qué hay de malo en usar find . -name "bar*.c" -exec ack foo {} \;? No tiene nada de especial grep, puede usar cualquier comando con find's -exec.
terdon
1
@terdon find también busca en los directorios de control de versiones y no quiero eso.
compie
2
Luego, edite su pregunta y explique las limitaciones que necesita para solucionar.
terdon

Respuestas:

28

Buscando directorios

Según la sinopsis que se muestra en la página de manual, diría que sí, puede procesar un directorio, pero al mirar los interruptores no puede buscar solo un archivo basado en un patrón. Para eso tendrás que alistarte find. El comando ackincluye la opción --files-from=FILEpara que se pueda alimentar una lista de archivos find.

sinopsis

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

uso

   --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.

Existe la --ignore-file=opción que puede darle lo que desea, pero parece un poco difícil de usar.

   --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".

Buscar tipos específicos de archivos

La única otra forma en que puedo concebir hacer esto ackes usar su --typeinterruptor:

   --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.

Para ver qué tipos están disponibles:

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

formato. Por ejemplo, tanto --type = perl como --perl funcionan. - [no] cpp .cpp .cc .cxx .m .hpp .hh .h .hxx - [no] objcpp .mm .h - [no] perl .pl .pm .pod .t .psgi; primera línea coincide con /^#!.*\bperl/ - [no] perltest .t

Ejemplos

Encuentre todos los archivos Perl, en función del nombre de archivo (* .pm, * .pl, * .t y * .pod) y la línea shebang.

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

Encuentra todos los archivos C ++:

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

Buscando a foo en el bar * .c

Entonces, ¿cómo puedes lograr lo que quieres? Bueno, probablemente tengas que usar findpara hacer esto:

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

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

También puede usar ackla capacidad de buscar archivos que coincidan con un patrón determinado en sus nombres de archivo (usando -g <pattern>), y luego pasar esta lista a una segunda invocación de ackusar -xo --files-from=-...

Utilizando -x:

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

dir1/bar1.c
1:foo
2:foo

Utilizando -files-from=-:

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

dir1/bar1.c
1:foo
2:foo

En cualquier caso, estamos haciendo coincidir los nombres de archivo que desea con esta expresión regular:

\bbar.*.c$

Esto coincide con los archivos cuyo nombre es bar.*cy final después del .cuso final de la línea de anclaje, $. También buscamos asegurarnos de que los nombres tengan un carácter de límite usando \b. Esto fallará para los archivos que contienen caracteres de límite como $bar.co %bar.cpor ejemplo.

slm
fuente
1
Esto no responde a mi pregunta (busque foo en todos los archivos llamados "bar * .c")
compie
1
@compie: lo hace en el sentido de que te estoy mostrando cómo obtener una porción de los archivos que deseas. Para buscar cpparchivos ack --type=cpp <string>. Consulte ackla página de manual para obtener más información sobre todo esto. Pero lo que básicamente te digo es que no puedes buscar de ackla manera que quieras.
slm
1
entonces responde en el sentido de que muestra ackque no puede hacer lo que se le pidió.
xealits
@xealits - esta parte lo responde: Buscando foo en el bar * .c find adir -iname "bar*.c" | ack --files-from=- foo . O, si solo quiere un archivo:echo "barBaz.c" | ack --files-from=- foo
alexanderbird
2
tldr; ack -g '\bbar.*.c$' | ack -x foo
chim
14

Es fácil si se conoce el tipo de archivo, y ack conoce muchos tipos de archivos. Entonces, si, por ejemplo, desea buscar solo en archivos C, entonces puede hacerlo:

ack --cc 'string'

Pero si no es una de las extensiones conocidas, debe definir su propio tipo. Esto debería funcionar:

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

Tenga en cuenta que necesita tanto --type-set como --barc.

(Gracias a Andy, quien también ayudó con esto en la lista de correo).

Amir E. Aharoni
fuente
1
Ok, eso funciona, pero usar find y 'ack -x' es más simple. Creo que me quedaré con eso.
Compie
Agregar un enlace a los documentos aquí sería útil. man ackrealmente no ayuda y no tiene cccuando busco eso.
YPCrumble
ack --help = types Hay todo lo que puedo pensar allí. Principalmente lo uso para Python y Ada.
David Boshton
6

"¿Qué hay de nuevo en ack 2?" http://beyondgrep.com/ack-2.0/

con ack 2.0, puede usar el nuevo -x para canalizar nombres de archivos de una invocación de ack a otra.

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

Solo que no puedo hacer que funcione:

% 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.

Por lo tanto, parece que -g necesita una expresión regular, mientras que yo quiero una opción de estilo 'glob' ...

compie
fuente
2
Sí, -gtoma una expresión regular, no un globo. Podrías escribir tu expresión regular como .*bar.*\.c$.
Andy Lester
2
@AndyLester Gracias por confirmar. ¿Te gusta la idea de una opción 'glob' para ack?
Compie
4

Esto parece ser el más rápido y seguro:

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

-x Lea la lista de archivos para buscar desde STDIN.

Sin embargo, si es probable que el archivo esté en su locatebase de datos, esto sería aún más rápido:

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

Localizar también puede aceptar expresiones regulares de la vieja escuela, un poco dolorosas, pero si está buscando carchivos, puede ser necesario. p.ej

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

Eso solo se traduce en: "buscar todos los nombres de archivo que terminan en c, cpp, h, hpp o cc".

Orwellophile
fuente
2

Ack no admite la selección de archivos de estilo global. Como realmente extraño esto, creé un pequeño script de shell ackg :

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

Ahora puedes usar el comando:

ackg foo "bar*.c"

Pero tenga en cuenta: desafortunadamente, esto también buscará en los directorios de control de versiones (por ejemplo: .git).

compie
fuente
2

Esto se puede hacer con la -Gopción de ag, el buscador de plata (un clon mejorado de ack-grep).

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

Notas:

  • agusa la sintaxis de expresión regular PCRE , por lo que la expresión regular debe ser en bar.*\.clugar de bar*.c.
  • La -Gopción debe preceder al término de búsqueda, ya que cualquier cosa después de eso se interpreta como un nombre de archivo.
Jon Olav Vik
fuente
1

¿Desea solo un cierto patrón, o solo quiere archivos fuente C?

Si desea todos los archivos C, use

ack --cc foo

Si desea todos los archivos C y NO los archivos de encabezado C, use

ack --cc --no-hh foo
Andy Lester
fuente
1

La respuesta de @chim es la mejor, pero está escrita como un comentario, así que la vuelvo a publicar como una respuesta formal ...

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

Me gusta el hecho de que puedes regex el camino ...

una varilla
fuente