http://qs321.pair.com?node_id=377321

pcassell has asked for the wisdom of the Perl Monks concerning the following question:

So I have been playing around with perl the last week and trying to get a handle on it. Im hardly able to write anything decent without having perlmonks or perldoc open though, but I am learning!

Wanted to know what/how I could improve the following code or in what ways you would have done it differently. I really like seeing more than one approach, and I need help pointing out my pitfalls.

Its very simple code, just traverses a specified directory and changes the permissions for files and/or directories. It is my perl way of doing the following with GNU Find.
find [base directory] -type [f|d] -exec chmod [perm] {} \;

Here it is, input greatly appreciated. Im still a newbie though.
#!/usr/bin/perl -w use strict; use Getopt::Long; use File::Find; my %opts = ( verbose => '', directory => './', filemode => '', dirmode => '' ); if(!GetOptions('verbose' => \$opts{'verbose'}, 'filemode:s' => \$opts{'filemode'}, 'dirmode:s' => \$opts{'dirmode'})) { usagedie(); } #make sure each argument passed is either a file or directory foreach (@ARGV) { usagedie() if (!-f || !-d); } #we need to perform at least one option usagedie() if (!$opts{'filemode'} && !$opts{'dirmode'}); #use default directory if argument not set on command line find(\&fileop, $opts{'directory'}) unless @ARGV; find(\&fileop, @ARGV) if @ARGV; sub fileop { #set the right permissions based on if a file or a directory #only set permissions of the mode is set chmod oct($opts{'filemode'}), $_ if -f && $opts{'filemode'}; chmod oct($opts{'dirmode'}), $_ if -d && $opts{'dirmode'}; print $File::Find::name . "\n" if $opts{'verbose'} && (-f || -d); } sub usagedie { print "Usage: setperm.pl [-v] [-f perm] [-d perm] [Directory]\n"; print "-v, --verbose\t show files changed by script\n"; print "-f, --filemode\t octal mode to change files to\n"; print "-d, --dirmode\t octal mode to change directories to\n"; exit(0); }

Updated Code:
#!/usr/bin/perl -w use strict; use Getopt::Long; use File::Find; my %opts = ( verbose => '', filemode => '', dirmode => '' ); if (!GetOptions(\%opts, 'verbose', 'filemode:s', 'dirmode:s')) { usagedie(); } #make sure each argument passed is either a file or directory foreach (@ARGV) { usagedie() if (!-f && !-d); } #we need to perform at least one option usagedie() if (!$opts{'filemode'} && !$opts{'dirmode'}); #make sure correct octal values were used $opts{'filemode'} = chkperm('File', $opts{'filemode'}); $opts{'dirmode'} = chkperm('Directory', $opts{'dirmode'}); #use default directory if argument not set on command line find(\&fileop, @ARGV ? @ARGV : './'); sub fileop { #set the right permissions based on if a file or a directory #only set permissions if the mode is set if (-f && $opts{'filemode'}) { chmod $opts{'filemode'}, $_; } elsif (-d && $opts{'dirmode'}) { chmod $opts{'dirmode'}, $_; } else { #dont print if we didnt modify file perms return; } print $File::Find::name . "\n" if $opts{'verbose'}; } sub chkperm { my ($type, $perm) = @_; #zero length strings are defined values #our $perm 's by default are set to '' if ($perm) { return ($perm =~ /^[0-7]{4}$/) ? oct($perm) : die "Incorrect $type mode $perm\n"; } return; } sub usagedie { print "Usage: setperm.pl [-v] [-f perm] [-d perm] [Directory]\n"; print "-v, --verbose\t show files changed by script\n"; print "-f, --filemode\t octal mode to change files to\n"; print "-d, --dirmode\t octal mode to change directories to\n"; exit(1); }

Thanks to all of ya'll who helped out. I tested it a few times and it worked perfect, I will test more, but I wanted to show what the combined efforts did to the script before it went off the front page.

I'll be adding an exclude option soon too. This will allow you to exclude directories or files. Thanks!