Here's something i put together over the past two days to solve a problem with my SSH client's file uploader (it sets permissions screwy, and i was constantly having to change all the permissions by hand whenever i uploaded a file or directory tree). perhaps it will be of some service to some of you working with linux/unix dealing with a constant need to chmod large amounts of files (it supports recursion through an entire directory tree, which is mainly why i wrote it).

save it as 'pchmod.pl' and type 'pchmod.pl -h' to get a the 'help' menu.
#!/usr/bin/perl =pod pchmod - perl recursive chmod by: scott@furt.com nothing in life is guaranteed, especially not this code. use it however you want. if you like it, perhaps send me an email and tickle my ego :-) =cut use strict; use vars qw($dir $rec $test $verb $igdir $foll $c $help); use File::Find; my $DBG = 0; #print out some misc. information my $VERSION = 2; #approx. version number #turn verbosity ON by default $verb++; # # some configurations (chosen with -c option) # my %cfg = ( 'default' => { 'dfile' => '0644', #default file mode 'ddir' => '0755', #default directory mode 'modes' => { 'cgi' => '0755', #mode for .cgi files 'pl' => '0755', #mode for .pl files 'sh' => '0755', #mode for .sh files }, }, 'group' => { 'dfile' => '0664', 'ddir' => '0775', 'modes' => { 'cgi' => '0775', 'pl' => '0775', 'sh' => '0775', }, }, 'secure' => { 'dfile' => '0600', 'ddir' => '0700', 'modes' => {}, }, ); # ----------------------------------------- #get options from cmd line while ( my $opt = shift ) { $rec++ if (($opt eq '-r')||($opt eq '-R')); $test++ if (($opt eq '-t')||($opt eq '-T')); $igdir++ if (($opt eq '-i')||($opt eq '-I')); $verb-- if (($opt eq '-V')||($opt eq '-v')); $foll++ if (($opt eq '-s')||($opt eq '-s')); $help++ if ($opt =~ /^(-?)-h/i); $dir = $opt if ($opt !~ /^\-/); $c = $1 if ($opt =~ /^\-c(.*)/); } #default directory is the current one. $dir ||= '.'; #default config to use is 'default' $c ||= 'default'; #set variables to selected config's default file and dir mode my $dfile = $cfg{$c}{'dfile'}; my $ddir = $cfg{$c}{'ddir'}; #print out "help" message (if help ON) if ($help) { &help(); } #spew some info about current options (if verbosity ON) if ($verb) { &show_env(); } # ----------------------------------------- #start your engines. find ( { 'wanted' => sub { &wanted($_); }, 'follow' => $foll, #follow symlinks? 'no_chdir' => 1, #dont execute a chdir() when # it goes into subdirectories }, $dir #directory to start processing in ); exit; sub wanted { my ($name) = @_; my ($vname) = $File::Find::name; if ($DBG) { print "Name: $name\n"; print "Dir: ". $File::Find::dir ."\n"; print "Topdir: ". $File::Find::topdir ."\n"; } if (!$rec) { return if ($File::Find::topdir ne $File::Find::dir); } print "Attempting: [$name] " if ($DBG); #looking at a file if (-f $name) { $name =~ /.*\.(.*?)$/; if ($cfg{$c}{'modes'}{$1}) { my $mode = $cfg{$c}{'modes'}{$1}; print "chmod $mode $vname\n" if ($test || $verb); chmod (oct($mode), $name) if (!$test); return 1; } #chmod to default value #if not already chmodded chmod(oct($dfile), $name) if (!$test); print "chmod [$dfile] $vname\n" if ($test || $verb); } #looking at a directory elsif ( (-d $name) && ($name ne '.') && ($name ne '..') ) { chmod (oct($ddir), $name) if (!$test || !$igdir); print "chmod /$ddir/ $vname\n" if (($test || $verb) && !$igdir) +; } } # show current options sub show_env { print "Using Options: * Dir $dir * Config: '$c' * Recursing ". (($rec) ? "ON" : "OFF") ." * Verbosity ". (($verb) ? "ON" : "OFF") ." * Ignore Dirs ". (($igdir) ? "ON" : "OFF") ." * Test Only ". (($test) ? "ON" : "OFF") ."\n "; } #show the help message sub help { print "perl chmod (v$VERSION) Usage: pchmod <dir> [options] -r\t\tRecursive mode -v\t\tQuiet mode, doesnt print status messages -s\t\tFollow sym.links (NO by default) -i\t\tDo not chmod() directories -t\t\tTest mode, doesnt alter any files -c <string>\tUse <string> config values Configs available:\n"; map { print "\t\t-c$_\n"; print "\t\t\tdfile: $cfg{$_}{'dfile'}\n"; print "\t\t\tddir: $cfg{$_}{'ddir'}\n"; } (keys %cfg); exit(0); }

Replies are listed 'Best First'.
Re: recursive perl chmod
by a (Friar) on Mar 02, 2001 at 10:00 UTC
    Very nice. I get a few 100:
    Exiting subroutine via next at pchmod.pl line 108.
    w/ -w on. Changing next to return seems to solve that.

    a

      Doh!
      that should teach me to use -w more often :)
      thanks for the fix... i updated the node with 'return' instead of 'next'
        Wow, what A COOL script! Just saved me about 3-4 hours having to re-chmod loads of files/folders.

        Thanks for sharing!

        Andy
(ichimunki) re: recursive perl chmod
by ichimunki (Priest) on Mar 02, 2001 at 17:41 UTC
    Although this may not save you much here, you may want to look at Getopt::Std for grabbing command line options (maybe even Getopt::Long).

    Also, if you are distributing a replacement for a unix system tool, you might want to be more verbose about what this does that it doesn't. Because when I hear "recursive chmod" I think chmod 755 cgi-bin --recursive. Added: of course, by looking at it, I see that it does a great job of taking different file types and chmod'ing them appropriately. Cool.
      i didnt use GetOpt, becuase i only have a few command line options to read in and didnt really see the need for using a separate module to do what i could do in ~8 lines of perl code... if i add anymore options in the future, i'll probably end up using GetOpt.

      i dont intend this to be a replacement for unix 'chmod' either, but rather a tool to enhance the functionality of chmod, by allowing a bit more control over what gets chmodded. it allows the user to chmod an arbitrary number of files to an arbitrary number of modes all at one time (instead of chmodding all directories to 755, then chmod 644 -R *, then chmod 755 *.pl *.sh *.cgi, etc... everytime a directory tree needs to have permissions set)

      the biggest difference between (pchmod.pl -r) and (chmod {mode} -R *) is that this will differentiate between files of certain types and directories, rather than mindlessly chmod *everything* in the tree to one mode. all hell breaks loose here when someone accidentally chmod's a directory on the webserver to 644 and apache spews out Forbidden errors... :)

      it's also good for people that have a hard time remembering the syntax for chmod or aren't too familiar with linux... you can just add the necessary options into the %cfg hash and tell the person to type (pchmod -csomeconfig) and it'll set things the way you specified in the %cfg hash, instead of the person remembering the octal codes and such...