Portrait of Michael Limberger

Michael Limberger

Need me? Email mike@limberger.ca

Regex

Ack - Advanced Techniques

Going further with ack

You know the basics. Now the useful corners.

A file of defaults

Put common options in ~/.ackrc:

Show me.

--ignore-dir=node_modules
--ignore-dir=vendor
--ignore-dir=.cache
--ignore-file=match:/\.min\.js$/
--color
--type-set=mojo:ext:ep,pm,pl

Now every ack search automatically uses these settings. Ignore directories, ignore minified JS, always color, add a custom Mojolicious file type.

Several patterns

You can use alternation, or chain searches. ack "TODO" && ack "FIXME" runs the second only if the first found something. Or put the OR in the pattern:

ack "TODO|FIXME|HACK|XXX"

Exclude a directory tonight

ack --ignore-dir=logs "error"
ack --ignore-dir=backup --ignore-dir=tmp "pattern"

Only the match, not the line

ack -o "\d+" shows only the matching part. Given "There are 42 items and 17 more", output is 42, then 17. Incredibly useful for data extraction.

Unique matches: ack -o "\d+" | sort | uniq. Or with Perl, more portable: ack -o "\d+" | perl -ne 'print unless $seen{$_}++'.

$_ is Perl's default variable: the current line. $seen{$_}++ counts how many times we have seen this line. unless prints only the first time.

find, then ack

find . -name "*.log" -mtime -1 | xargs ack "ERROR"

Searches only .log files modified in the last day.

More than one line

By default, ack searches line by line. For multi-line patterns you need different tools. For most work, line-by-line is fine.

A workaround: ack -A 1 "function\s*$" finds function at end of line and shows the next line.

xargs

Show me.

ack -l "deprecated" | xargs wc -l
ack -l "TODO" | xargs grep -c "TODO"
ack -l "pattern" | xargs rm

Line counts of matching files. TODO counts per file. Delete matching files. That last one is careful territory.

Binary files

ack skips binary files by default. Include them with ack -a "pattern". Search only binary with ack --type=nofilter "pattern".

JSON-ish output

ack does not have built-in JSON output, but you can fake it:

ack -c "error" | perl -pe 's/^(.+):(\d+)$/{"file":"$1","count":$2}/'

For serious structured output, use ripgrep (rg) which has --json.

ack, grep, ripgrep

grep comes with your system. Basic but universal. ack is great for code, smart defaults, readable output. ripgrep is fastest, good for huge codebases, has JSON output.

For most people, ack is the sweet spot. Fast enough, and the smart defaults save tons of typing.

Auditing a codebase

You just inherited a codebase.

Show me.

ack -i "password\s*=|api_key\s*=|secret\s*="
ack "console\.log|print\s*\(|var_dump"
ack "\$_GET|\$_POST.*SELECT|.*\+.*SELECT"
ack "TODO|FIXME|HACK|XXX|BUG"
ack -L "^#!|Copyright|License"

Hardcoded secrets. Debug statements. SQL that might be injectable. TODO comments. Files without a shebang or a license line. \$_GET is Perl's CGI query hash. The backslash keeps the dollar sign literal.

Parsing AI output

You ran a batch vision analysis. Results are scattered across logs.

ack -o "(PASS|REJECT):\s*\w+" logs/
ack -o "confidence:\s*\d+\.\d+" logs/
ack -l "NEEDS_REVIEW" logs/
ack -o "REJECT:\s*\w+" logs/ | sort | uniq -c | sort -rn

Unix neighbors

ack "pattern" | head -20
ack "pattern" | tail -10
ack -c "pattern" | sort -t: -k2 -rn
ack -l "pattern" | wc -l
ack -o "pattern" | sort -u

Habits that help

Start broad, then narrow down. Better to see too much than miss something. Use -l first to see which files match, then investigate. -C 2 helps you understand matches without opening files.

Aliases in your shell config:

alias todos="ack 'TODO|FIXME|HACK'"
alias hardcoded="ack -i 'password|secret|api.?key'"