How to specify an ignore pattern with ack?
Asked Answered
S

2

7

I'm trying to search for lines containing 'foo' but these lines must not contain 'bar'. What's a good way to search with these parameters with ack?

I can do:

ack "foo" | grep -v "bar"

but it doesn't keep the nicely layed-out and coloured output of ack. Also, grep doesn't have good support for Perl regexes, unlike ack.

Suck answered 31/8, 2014 at 9:12 Comment(4)
@TomFenech Yes, terribly sorry. I corrected that a couple of mins after posting the question by for some reason it didn't persist.Suck
You can not use awk? It will do this with ease.Colenecoleopteran
@Colenecoleopteran ack is more suited for searching text files, particularly source code, and has a lot of features suited for this that make it a better tool for such tasks than awk. e.g. awk only supports POSIX ERE which is kind of a deal-breaker for me; on the other hand, ack supports Perl regex.Suck
Regex is not always important. I see again and again problem solved using awk without using regex. If your system does have awk try it out using time to see what is fastest like this time awk '/foo/ && !/bar/' file Then you can make a selection based on portability easy to understand and speedColenecoleopteran
D
8

You can use a slightly more complicated regular expression:

ack '^(?!.*bar).*(foo)' file

The (?!...) does a negative lookahead to ensure that "bar" is not on the line. Here's how it works:

  • ^ Start at beginning of the line.
  • (?!.*bar) Advance any number of characters until "bar" is found. If it is found, do not continue. Otherwise, return to the start of the line and continue.
  • .*(foo) Match (and capture) "foo" at any point between the beginning and end of the line.

Testing it out:

$ cat file
other foo bar
baz foo other
$ ack '^(?!.*bar).*(foo)' file
baz foo other
Detwiler answered 31/8, 2014 at 10:5 Comment(4)
Unlike the alternative suggestion of @jcl's answer this highlights only the search term instead of the whole line. I can try make an ack wrapper out of this somehow so that I don't have to type out that long regex. Thanks!Suck
No problem. The reason that only the word is highlighted is because I have surrounded it in a capturing group (foo). If you remove the parentheses, the whole line will be highlighted.Detwiler
Please could you explain why lines with "bar" are ignored even if their position in the line is after "foo"? I thought that a lookbehind looks behind the match pattern.Suck
Sure, I have added some explanation to my answer.Detwiler
A
5

One possibility is to use ack itself for the exclusion pass, and to reverse the order of invocation:

ack -v "bar" | ack "foo"

That will at least highlight the search term, although it won't get you quite the same formatting as just ack "foo" does.

Another possibility is to pack the two terms together in the same regex, using a negative lookahead for the exclusion term, as given in this answer:

Multiple patterns with ack-grep?

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

That will get you ack's per-file formatting, but the whole line will be highlighted as the search term.

Allergist answered 31/8, 2014 at 9:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.