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