When working on Linux servers, developers often need to locate files with specific extensions across entire directory trees. The standard approach using find combined with grep has a significant limitation - it misses hidden files (those beginning with a dot).
The command sequence:
find . -type f | grep -i *.php
misses hidden files because:
- The shell expands
*.phpbefore grep sees it - Globbing patterns don't match dotfiles by default
The most efficient method uses find's built-in pattern matching:
find . -type f -iname "*.php"
This command:
- Recursively searches from current directory (.)
- Finds only regular files (-type f)
- Matches case-insensitive (-iname) PHP extensions
- Includes both hidden and visible files
For more complex scenarios:
# Find PHP files modified in last 7 days
find . -type f -iname "*.php" -mtime -7
# Find PHP files with specific permission
find . -type f -iname "*.php" -perm 0644
# Combine multiple extensions
find . -type f $-iname "*.php" -o -iname "*.phps"$
# Exclude certain directories
find . -type f -iname "*.php" -not -path "./vendor/*"
For very large directories, using -name instead of -iname (case-sensitive) can be slightly faster:
find . -type f -name "*.php" -o -name ".*.php"
The -o operator combines both patterns, though -iname is generally more readable and maintainable.
When working with Linux servers, developers often need to locate files matching specific patterns. The common approach using find . -type f | grep -i *.php has a significant limitation - it misses hidden files (those beginning with a dot).
The issue stems from how shell globbing and grep patterns interact. Hidden files (like .config.php) don't match the simple *.php pattern because:
1. The dot prefix makes them "hidden" in Unix-like systems
2. Shell globbing doesn't include dotfiles by default
3. The grep pattern needs proper escaping for dots
Instead of piping to grep, we can use find's built-in pattern matching:
find . -type f -name "*.php" -o -name ".*.php"
This command efficiently finds both:
- regular.php
- .hidden.php
- subdir/normal.php
- subdir/.config.php
For case-insensitive matching (PHP, php, Php):
find . -type f $-iname "*.php" -o -iname ".*.php"$
You can extend this to include additional criteria like file size or modification time:
find . -type f $-iname "*.php" -o -iname ".*.php"$ -mtime -7 -size +1k
This finds PHP files (hidden or not) modified in the last 7 days and larger than 1KB.
For complex patterns, consider regular expressions:
find . -type f -regex ".*/\..*\.php$\|.*\.php$" -print
The native find patterns execute faster than piping to grep because:
- No additional process spawned (grep)
- Find's internal matching is optimized
- Less data transferred through pipes