# How can I further optimize this Perl script for finding all unowned files and directories on Unix?

(Originally posted on Stack Overflow)

Following my findings and suggestions in my other post How to exclude a list of full directory paths in find command on Solaris, I have decided to write a Perl version of this script and see how I could optimize it to run faster than a native find command. So far, the results are impressive!

The purpose of this script is to report all unowned files and directories on a Unix system for audit compliance. The script has to accept a list of directories and files to exclude (either by full path or wildcard name), and must take as little processing power as possible. It is meant to be run on hundreds of Unix system that we (the company I work for) support, and has be able to run on all those Unix systems (multiple OS, multiple platforms: AIX, HP-UX, Solaris and Linux) without us having to install or upgrade anything first. In other words, it has to run with standard libraries and binaries we can expect on all systems.

I have not yet made the script argument-aware, so all arguments are hard-coded in the script. I plan on having the following arguments in the end and will probably use getopts to do it:

-d = comma delimited list of directories to exclude by path name
-w = comma delimited list of directories to exclude by basename or wildcard
-f = comma delimited list of files to exclude by path name
-i = comma delimited list of files to exclude by basename or wildcard
-t:list|count = Defines the type of output I want to see (list of all findinds, or summary with count per directory)


Here is the source I have done so far:

#! /usr/bin/perl
use strict;
use File::Find;

# Full paths of directories to prune
my @exclude_dirs = ('/dev','/proc','/home');

# Basenames or wildcard names of directories I want to prune
my $exclude_dirs_wildcard = '.svn'; # Full paths of files I want to ignore my @exclude_files = ('/tmp/test/dir3/.svn/svn_file1.txt','/tmp/test/dir3/.svn/svn_file2.txt'); # Basenames of wildcard names of files I want to ignore my$exclude_files_wildcard = '*.tmp';
my %dir_globs = ();
my %file_globs = ();

# Results will be sroted in this hash
my %found = ();

# Used for storing uid's and gid's present on system
my %uids = ();
my %gids = ();

# Callback function for find
sub wanted {
my $dir =$File::Find::dir;
my $name =$File::Find::name;
my $basename =$_;

return if -l $name; # Search for wildcards if dir was never searched before if (!exists($dir_globs{$dir})) { @{$dir_globs{$dir}} = glob($exclude_dirs_wildcard);
}
if (!exists($file_globs{$dir})) {
@{$file_globs{$dir}} = glob($exclude_files_wildcard); } # Prune directory if present in exclude list if (-d$name && in_array(\@exclude_dirs, $name)) {$File::Find::prune = 1;
return;
}

# Prune directory if present in dir_globs
if (-d $name && in_array(\@{$dir_globs{$dir}},$basename)) {
$File::Find::prune = 1; return; } # Ignore excluded files return if (-f$name && in_array(\@exclude_files, $name)); return if (-f$name && in_array(\@{$file_globs{$dir}},$basename)); # Check ownership and add to the hash if unowned (uid or gid does not exist on system) my ($dev,$ino,$mode,$nlink,$uid,$gid) = stat($name);
if (!exists $uids{$uid} || !exists($gids{$gid})) {
push(@{$found{$dir}}, $basename); } else { return } } # Standard in_array perl implementation sub in_array { my ($arr, $search_for) = @_; my %items = map {$_ => 1} @$arr; return (exists($items{$search_for}))?1:0; } # Get all uid's that exists on system and store in %uids sub get_uids { while (my ($name, $pw,$uid) = getpwent) {
$uids{$uid} = 1;
}
}

# Get all gid's that exists on system and store in %gids
sub get_gids {
while (my ($name,$pw, $gid) = getgrent) {$gids{$gid} = 1; } } # Print a list of unowned files in the format PARENT_DIR,BASENAME sub print_list { foreach my$dir (sort keys %found) {
foreach my $child (sort @{$found{$dir}}) { print "$dir,$child\n"; } } } # Prints a list of directories with the count of unowned childs in the format DIR,COUNT sub print_count { foreach my$dir (sort keys %found) {