49

Can ack search only through files that match a specific 'glob' pattern (eg: search for foo in all files named "bar*.c"). The command

ack foo "bar*.c"

only works in the current directory.

Note: I know it's possible with find -exec:

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

But I would like a small and simple ack command, because find doesn't skip version control directories.

  • 2
    I don't understand, what's wrong with using find . -name "bar*.c" -exec ack foo {} \;? There's nothing special about grep, you can use any command with find's -exec. – terdon Apr 24 '14 at 11:11
  • 1
    @terdon find also searches through version control directories and I don't want that. – compie Apr 24 '14 at 13:41
  • 2
    Then please edit your question and explain the limitations you need to work around. – terdon Apr 24 '14 at 13:44
28

Searching directories

Based on the synopsis shown in the man page I would say yes it can process a directory, but looking at the switches it cannot look for just a file based on a pattern. For that you'll have to enlist find. The command ack does include the option --files-from=FILE so that it can be fed a list of files from 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.

There is the --ignore-file= option which may give you what you want but seems a bit of a pain to actually use.

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

Searching specific types of files

The only other way I can conceive of doing just this via ack is to use its --type switch:

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

To see what types are available:

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

format. For example, both --type=perl and --perl work. --[no]cpp .cpp .cc .cxx .m .hpp .hh .h .hxx --[no]objcpp .mm .h --[no]perl .pl .pm .pod .t .psgi; first line matches /^#!.*\bperl/ --[no]perltest .t

Examples

Find all the Perl files, based on both the filename (*.pm, *.pl, *.t and *.pod) and the shebang line.

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

Find all the C++ files:

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

Searching for foo in bar*.c

So then how can you accomplish what you want? Well you'll have to likely use find to do this:

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

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

You can also use ack's ability to search for files that match a given pattern in their filenames (using -g <pattern>), and then pass this list to a second invocation of ack using -x or --files-from=-..

Using -x:

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

dir1/bar1.c
1:foo
2:foo

Using -files-from=-:

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

dir1/bar1.c
1:foo
2:foo

In either case we're matching the filenames that you want using this regex:

\bbar.*.c$

This matches files whose name is bar.*c and end after the .c using the end of line anchor, $. We also look to make sure that the names have a boundary character using \b. This will fail for files that contain boundary characters such as $bar.c or %bar.c for example.

  • 1
    This doesn't answer my question (search for foo in all files named "bar*.c") – compie Apr 24 '14 at 14:37
  • 1
    @compie - it does in the sense I'm showing you how to get a slice of the files you want. To search for cpp files ack --type=cpp <string>. See ack's man page for more on all this though. But what I'm basically telling you is that you cannot search using ack the way that you want. – slm Apr 24 '14 at 15:43
  • 1
    so it does answer in the sense that it shows ack cannot do what was asked. – xealits Aug 5 '14 at 13:49
  • @xealits - this part answers it: Searching for foo in bar*.c find adir -iname "bar*.c" | ack --files-from=- foo. Or, if you just want one file: echo "barBaz.c" | ack --files-from=- foo – alexanderbird Aug 31 '17 at 22:46
  • 2
    tldr; ack -g '\bbar.*.c$' | ack -x foo – chim Oct 12 '17 at 14:26
14

It's easy if the file type is known, and ack knows a lot of file types. So if, for example, you want to only search in C files, than you can do:

ack --cc 'string'

But if it's not one of the known extensions, you need to define your own type. This should work:

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

Note that you need both --type-set and --barc.

(Thanks to Andy, who also helped with this on the mailing list.)

  • 1
    Ok that works but using find and 'ack -x' is simpler. I think I will stick with that. – compie Apr 25 '14 at 6:58
  • Adding a link to the docs here would be helpful. man ack doesn't really help and doesn't have cc when I search that. – YPCrumble Apr 16 '16 at 15:19
  • ack --help=types There's everthing I can think of there. I mostly use it for Python and Ada. – David Boshton Feb 23 '17 at 9:58
6

"What's new in ack 2?" http://beyondgrep.com/ack-2.0/

with ack 2.0, you can use the new -x to pipe filenames from one invocation of ack into another.

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

Only I can't get it to work:

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

Thus it seems -g needs a regex, while I want a 'glob' style option...

  • 2
    Yes, -g takes a regex, not a glob. You could write your regex as .*bar.*\.c$. – Andy Lester Apr 24 '14 at 15:37
  • 2
    @AndyLester Thanks for confirming. Do you like the idea of a 'glob' option for ack? – compie Apr 25 '14 at 6:59
3

This would seem to be the fastest and safest:

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

-x Read the list of files to search from STDIN.

However, if the file is likely to be in your locate database, this would be even quicker:

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

Locate can also accept old-school regular expressions, a little painful, but if you're looking for c files, it might be required. e.g.

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

That just translates to: "search all filename that end in c, cpp, h, hpp or cc."

2

Ack doesn't support glob style file selection. Since I really miss this I created a small shell script ackg:

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

Now you can use the command:

ackg foo "bar*.c"

But note: this will unfortunately also search in version control dirs (eg: .git).

2

This can be done with the -G option to ag, the silver searcher (an enhanced clone of ack-grep).

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

Notes:

  • ag uses PCRE regular expression syntax, so the regexp must be bar.*\.c instead of bar*.c.
  • The -G option needs to precede the search term, as anything after that is interpreted as a filename.
1

Do you want only a certain pattern, or do you just want C source files?

If you want all C files, use

ack --cc foo

If you want all C files and NOT C header files use

ack --cc --no-hh foo
1

@chim 's answer is the best, but its written as a comment so I'm reposting as a formal answer...

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

I like the fact that your can regex the path...

0

Using zsh you can do:

ls **/a*.txt | ack --files-from=- something

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.