This is the second time I highlight Andy Lester’s work on my blog. Back in 2013 I reviewed his book Land the Tech Job You Love which is my favorite tech career guide. Now I want to highlight his open-source tool ack which is “a tool like grep, optimized for programmers”. I use it every day and I have observed many times that people who don’t use it are slower at tasks that require locating files.
The ack website has a page “Top 10 reasons to use ack for source code” which I don’t want to repeat here. All of these reasons are true in my experience. I’ll just go through my personal main reasons to use ack.
Grep can search in explicitly listed files, or in all files in a directory tree. Ack can search in files of certain types under a directory tree.
I used to do find . -name "*.xml" -exec grep -H foo '{}' \;
. I guess grep foo
$(find ...)
would have been a bit easier, I learned this later.
Now I just do ack --xml foo
.
And it’s even better than the find version because it doesn’t search in .git
and similar directories. This unclutters the output and makes it run
faster. Perl’s optimized regex engine is pretty fast anyways.
For every file type --foo
you can also request --nofoo
to exclude it.
For certain types such as --shell
ack will even check the shebang line of
files that don’t have an extension.
What’s more, while there are many predefined file types, you can define your own, and they are not limited to a single extension.
You can also use this functionality to simple list all files of a type, for
example to pipe them into another command. Simply add -f
: ack -f --java
--xml
.
If you’d like to only consider files in subdirectories matching a certain
pattern, ack has you covered as well. ack -g 'src/main'
searches only in
src/main folders.
These are just the main ways to tell ack which files you’re interested in. Combined, they save me a lot of time when locating files.
I mainly work in Java these days. My project root has a bunch of Maven modules
in it, each in its own subdirectory. Inside each of those is a src/main
, a
src/test
, and a target
folder for build artefacts.
First, I don’t want to grep through the build artefacts ever. So I added
--ignore-dir=target
to my .ackrc
. I also want to ignore IntelliJ’s internal
project files, ending in .iml, which I ignore with --ignore-file=match:\.iml$
.
Second, I tend to search a limited number of sets of files. Usually it’s either all files, or only Java files, or only XML files (Spring configuration). And sometimes I want to search either of those only in main code, or only in test code. I addressed this with a number of bash aliases that allow me to do all that in two or three keystrokes. (Yes, IntelliJ can do this as well, but I have other issues with it, and for many tasks the command line is superior.)
alias aj='ack --java'
alias am='ack -v -g '\''src/test'\'' | ack -x'
alias ajm='ack --java -v -g '\''src/test'\'' | ack -x'
The first one searches only Java files, the second one ignores src/test, and the
third one combines those two. The aliases for XML and src/test are equivalent. I
don’t use -g src/main
for non-test code, but -v -g src/test
which means
“everything but src/test`. That way it also searches top-level files that are
neither in main nor in test.
So, for example, axm FooFactory
gives me all occurrences of FooFactory in XML
and related files, in src/main of any Maven module.
We do need a small clutch here because ack can only either select files by path
(-g
), or search in files, but not both together. So we invoke it twice, once
to select the files and once to search in them.
The ack website already lists this: ack is fast. But I’d like to emphasize that
it’s fast not just in terms of runtime, but in interaction time. Instead of
typing “grep -R” you type “ack”, many times a day. And with its ways of
narrowing down the search as shown above, it obsoletes a whole bunch of piped
shell expressions involving find
and friends. I simply get to hit return
sooner, saving time and mental effort.
Another point that doesn’t immediately seem related to speed is ack’s formatting of its output. When piped to another process, it’s mostly like grep’s. But when ack writes to the console, it formats for readability, intelligently using line breaks, colors, and highlighting. That makes it faster for me to scan the results of my search.
ack’s documentation is great, which allows everyone to get the most out of
it. The “almost” is because it’s a bit scattered. There’s ack --help
which
lists the main options, useful as a quick reference. Then there’s the man page,
also available via ack --man
(useful on Windows). That distinction makes
sense, but there’s a third place, the full documentation on the ack
website. That’s something to be aware of when looking for info. It helps,
though, that Stackoverflow has a large number of ack questions, many answered by
Andy himself.
ack mostly uses the same flags than grep, and it behaves like grep. So all the
flags you’re used to, like -i
and -v
, are there. I never had an issue
switching between the two.
ack is one of those tools that may not sound all that impressive at first. After all, it’s a glorified grep. But, in my experience, with ack the sum is greater than its parts. The different improvements over grep work together and accumulate to make a significant difference. I think and type less than with grep and have more brainpower available for my actual task.