Searching Multiple Patterns (Words) with Ack

Searching multiple patterns (words) with ack?

This should be enough:

ack -R 'string1|string2'

As -R is the default, you can omit it:

ack 'string1|string2'

From man ack:

-r, -R, --recurse

Recurse into sub-directories. This is the default and just here for
compatibility with grep. You can also use it for turning --no-recurse
off.


If you want to get the pattern from a file, say /path/to/patterns.file, you can use:

ack "$(cat /path/to/patterns.file)"

or equivallently:

ack "$(< /path/to/patterns.file)"

I cannot find an exact equivalent to grep -f.

ack - search for multiple patterns (logical AND)


 /foo/s && /bar/s && /baz/s

can be written as

 /^(?=.*?foo)(?=.*?bar)(?=.*?baz)/s

We don't actually need a look ahead for the last one.

 /^(?=.*?foo)(?=.*?bar).*?baz/s

And since we don't care which instance of the pattern is matched if there are more than one, we can simplify that to

 /^(?=.*foo)(?=.*bar).*baz/s

How can I use ack to find files that contain two patterns?

I take it from your question that you want files that contains pattern1 and also contain pattern2, even if they are on different lines.

Here's one way to do it:

ack -l pattern2 $(ack -l pattern1)

Here's another:

ack -l pattern1 | ack -l -x pattern2

The -x says "Get the list of files to search from standard input, as if I were the xargs program." (This is assuming you're using ack 2.x or higher)

Multiple patterns with ack-grep?

ack uses Perl regular expressions, and those allow lookahead assertions:

^(?!.*bar).*foo.*$

will match a line that contains foo but doesn't contain bar.

I'm not familiar with the usage of ack, but something like this should work:

ack '^(?!.*bar).*foo.*$' myfile

grep/ack on Mac OS X finding multiple strings and respecting file types

Here's a script that combines the closely-named awk and ack commands:

find . -iname '*.r' | while read file; do
awk '
BEGIN { IGNORECASE=1; sawWordA = 0; sawWordB = 0 }
/wordA/ { sawWordA = 1 }
/wordB/ { sawWordB = 1 }
sawWordA && sawWordB { exit } # stop reading lines if both matches seen
END { exit !(sawWordA && sawWordB) }
' \
"${file}" \
&& ack --nofilter -H -i 'wordA|wordB' "${file}"
done

The awk command...

  • Lists only the files where both strings appear irrespectively of the order
  • Ignores the case

...and the ack command...

  • Prints the lines where the strings appear
  • Highlights the found words in colour
  • Prints the file name as a header and lines under the header inspired by the ack options
  • Ignores the case

The awk script sets flags if there are search string matches. If both strings have been matched, then the snippet exit !(sawWordA && sawWordB) will return 0. If awk returns 0, then the ack command runs.

The ack --nofilter option tells ack to avoid reading from STDIN. Otherwise, ack would try to use the STDIN that the read command is using.

In the comments, Konrad asked how to use the above code when passing in variables in a shell script. Below is an example:

#!/bin/sh

if [ $# -ne 2 ]; then
echo Usage: $0 {string1} {string2}
E_BADARGS=65
exit $E_BADARGS
fi

find . -maxdepth 1 -iname '*.r' | while read file; do
awk "
BEGIN { IGNORECASE=1; sawArg1 = 0; sawArg2 = 0 }
/$1/ { sawArg1 = 1 }
/$2/ { sawArg2 = 1 }
sawArg1 && sawArg2 { exit } # stop reading lines if both matches seen
END { exit !(sawArg1 && sawArg2) }
" \
"${file}" \
&& ack --nofilter -H -i "$1|$2" "${file}"
done

The above example doesn't escape any special characters in the arguments provided to the script. If escaping is needed, the script can be modified as needed.

How do I search for the string '--branch' using ack?

Ack uses the Getopt::Long module to process command line arguments, so it supports the -- option to indicate that you want option processing to end at that point in the argument list.

ack -a -- --branch

should therefore work (it does for me).

How can I tell ack to search in two distinct directories?

Yes, you can specify any number of files or directories you want to search.

Look at the first line of ack --help.

Usage: ack [OPTION]... PATTERN [FILES OR DIRECTORIES]

(The ack manpage has a similar usage statement)

So you can do

ack needle this_haystack/ that/haystack/ other/haystack*.txt

ack regex: Matching two words in order in the same line

You want to find word_1, followed by anything, any number of times, followed by word_2. That should be

word_1.*word_2

You seem to be using * as it is often used in command line searches, but in regexes is it a quantifier for the preceding character, meaning match it at least 0 times. For example, the regex a* would match 0 or more as, whereas the regex a+ would match at least one a.

The regex metacharacter meaning "match anything" is ., so .* means "match anything, any number of times. See perlrequick for a brief introduction on the topic.



Related Topics



Leave a reply



Submit