Recursive Search
Historically, grep only read individual files. To search an entire directory tree, administrators had to use find to generate a list of files, and pipe them to xargs grep.
Modern GNU grep has recursive capabilities built-in, allowing it to crawl directories autonomously.
The Recursive Flags (-r vs -R)
To search a directory, pass the directory path as the final argument instead of a file, and use either -r or -R.
-r(Recursive, Default): Read all files under each directory recursively. It does NOT follow symbolic links (unless the symlink is passed explicitly on the command line).-R(Dereference Recursive): Read all files recursively, and DO follow all symbolic links.
# Recursively search the entire /var/log directory
grep -r "auth_failure" /var/log/
# Search the current directory
grep -r "TODO" .
-R)Using -R is highly dangerous if you do not understand your filesystem. If a directory contains a symlink pointing back to /, grep -R will enter an infinite loop and eventually crash or hang the server.
Always default to -r. Only use -R if you explicitly know you need to traverse linked directories and are confident no recursive loops exist.
Recursive Output Defaults
When you use -r or -R, grep automatically enables the -H flag (print filenames). Because you are searching a directory tree, the output will prefix every match with its relative or absolute path (depending on how you defined the target directory).
# Output of grep -r "listen" /etc/nginx/
/etc/nginx/nginx.conf: listen 80;
/etc/nginx/conf.d/default.conf: listen 443 ssl;
The Pipe Alternative (find | xargs grep)
While grep -r is convenient, it is not always the most efficient choice, especially when searching massive directories. grep has basic include/exclude flags, but it lacks the granular metadata filtering of find.
If you only want to search .log files modified in the last 7 days, grep -r cannot do this alone. You must revert to the classic POSIX pipeline:
# 100% precise directory search
find /var/log -type f -name "*.log" -mtime -7 -print0 | xargs -0 grep "ERROR"
(Note: We use -print0 and -0 to safely handle filenames with spaces, as discussed in the find documentation).