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

Hi, I've been playing around with a recursive chmod script and came upon something which I don't understand. I have a script which will change directories to 0755 and files to 0644. If I add warnings to the chmod line, the files will be made 0755. ?? What is happening? To simulate what I see, make a test directory say 1, and under that directory make subdirs 1,2,and 3. In 1/1 make a file named test, mode 0000. Make subdirs 1,2,and3 mode 0000 also.

Now here are the two scripts which you put in the top dir at the same level as 1, 2 and 3. The first script when run, will work properly, and make the subdirs 0755 and the test file 0644. The second script,(with warnings on chmod lines) will properly do the subdirs but it makes the test file 0755. What does the warnings do to cause this?

As a second question, why is the recursive chmod so difficult in Perl, where the unix chmod is so simple?

#!/usr/bin/perl -w use strict; use File::Find; my $topdir = shift || '.'; my $filemode = shift || 0644; my $dirmode = shift || 0755; find(\&doit, "$topdir"); #this sub works fine sub doit { return if -d and /^\.\.?$/; chmod($filemode, $_) if (-f $_); chmod($dirmode, $_) if (-d $_); }
#!/usr/bin/perl -w use strict; use File::Find; my $topdir = shift || '.'; my $filemode = shift || 0644; my $dirmode = shift || 0755; find(\&doit, "$topdir"); #this sub causes files to be 0755 instead of 0644 sub doit { return if -d and /^\.\.?$/; chmod($filemode, $_) if (-f $_) or warn $!; chmod($dirmode, $_) if (-d $_) or warn $!; }

Replies are listed 'Best First'.
Re: File::Find, chmod and warnings
by bart (Canon) on Oct 26, 2003 at 02:51 UTC
    As an aside just this warning. It seems to me like you want to read file modes from the command line. Be aware that these are strings, and that conversion of string to octal is not done automatically in a DMIMmy manner, in Perl. In English: if your parameter is "0755" and you treat this as a number, as with chmod, then perl will convert this to 755 (decimal). Ouch.

    You could use something like

    $mode= oct($mode) if /^0/;
    which can take care of octal ("0755"), hexadecimal ("0x1ED") and binary ("0b111101101") input, as well as decimal ("493"). But this doesn't give you the full user-friendliness of the command line tool chmod, which can parse letters combinations along with "+" and "-".

    There's basically two solutions, IMO: either you adopt some code, or a module, that deals with these modes (I'd think of one of tye's brainchildren, I forgot the exact name, but it's a node here), or you call the external program chmod with the passed on data. Not that I'd recommend the latter. Not without proper taint checking. (Is the argument really just an argument?)

Re: File::Find, chmod and warnings
by etcshadow (Priest) on Oct 26, 2003 at 00:47 UTC
    Does this explain it?
    [me@host]$ perl -le 'print 1 if (0) or warn 3'; 3 at -e line 1. 1
    You see, it's the order of operations on the if and the or... it's taking the "or warn $!" in the directory-chmoding line, and executing it (if, of course, it's a file... you know, lazy evaluation and all). "warn $!" returns true... so the overall condition of (-d $_) or warn $! evaluates to rue overall. In the end, this causes all files and directories to have their mode set first to 644 and then to 755. The fix would be to do this, instead:
    do { chmod($filemode, $_) if (-f $_) } or warn $!; do { chmod($dirmode, $_) if (-d $_) } or warn $!;

    ------------
    :Wq
    Not an editor command: Wq

      Or slightly more intuatively

      do{ chmod($filemode, $_) or warn $! } if -f $_; do{ chmod($dirmode, $_) or warn $! } if -d $_;

      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "Think for yourself!" - Abigail
      Hooray!

        Yeah, good point.
Re: File::Find, chmod and warnings
by hanenkamp (Pilgrim) on Oct 26, 2003 at 02:31 UTC

    Assuming that you're just trying to make everything readable/writable and directories executable, you could just do:

    chmod -R a=rX,u=rwX *

    I believe this to be POSIX compliant, but I could be wrong. This works under Linux. It will not dispell execute perms on plain files that already have them, but this could be rectified with a a-x before the rest of the symbolic mode expression.

Re: File::Find, chmod and warnings
by zentara (Cardinal) on Oct 26, 2003 at 16:18 UTC
    "doh!!!"...As usual when I post a question on a Saturday night my brain isn't at 100%......I knew what the problem was when I woke up this morning. Its funny how sleep can clarify things. The problem as I see it, is the precedence of "if" and "or" when using that "trailing if" idiom. So when I added the "or warn", the statement

    chmod($dirmode, $_) if (-d $_) or warn $!;

    if (-d $_) or warn $!;

    would always return true, since warn is always there, and everything, both files and directories were made 0755. My solution is (without Bart's correct fixes about octal() ) :

    #!/usr/bin/perl -w use strict; use File::Find; my $topdir = shift || '.'; my $filemode = shift || 0644; my $dirmode = shift || 0755; find(\&doit, "$topdir"); sub doit { return if $_ eq "." or $_ eq ".."; if(-f $_){chmod($filemode, $_)or warn $!} if(-d $_){chmod($dirmode, $_) or warn $!} }
A reply falls below the community's threshold of quality. You may see it by logging in.