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
*.php
before 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