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

I've got a perl script that I run from root but sudoed as apache. Like so:

# sudo -u apache /usr/bin/perl /path/to/script.pl

The script uses File::Find to parse a directory. Bascially it does something like this:
#!/usr/bin/perl -w use strict; use File::Find; my @dirList = ('/dir/to/parse'); find(\&wanted, @dirList); sub wanted { print "Processing: $File::Find::name\n"; } print "done.\n";
The problem is that if you are in the "/root" directory when you try this, you end up with the script doing some processing but then it dies out with: "Can't cd to /root: Permission denied". The same error shows up if you try to run the script in the root crontab with something like:

1 * * * * sudo -u apache /usr/bin/perl /path/to/script.pl

I looked around for a while to see if anyone else was running into this but my searches didn't return anything. After some playing around I came up with the following that seems to work for me:

#!/usr/bin/perl -w use strict; use File::Find; use FindBin; chdir($FindBin::RealBin); my @dirList = ('/dir/to/parse'); find(\&wanted, @dirList); sub wanted { print "Processing: $File::Find::name\n"; } print "done.\n";

Figured I'd post here in case it helps someone else, but I'm also curious if this is the best way to do this or what other options are.

Replies are listed 'Best First'.
Re: File::Find with sudo from root
by kyle (Abbot) on Jan 18, 2007 at 21:09 UTC

    I have two suggestions (neither of which is really worth fixing something that's not broken):

    1. It probably doesn't make a huge difference, but I'd chdir to '/dir/to/parse' instead of $FindBin::RealBin.
    2. You might also avoid having to chdir at all (but I'm not sure), if you just run this from apache's crontab instead of root's (crontab -e -u apache).

    But, whatever works.

      Ahhhhhh. didn't cross my mind on option 1. of just chdir to the path that find is starting in. I like that. Saves me from having to load the FindBin.

      The more I think about this though, the more it seems weird to have to do and chdir. File::Find works okay for a while then freaks out. I'm guessing there is a reason that it tries to read the directory that the script is in, but I can't think of what that would be. Not that I'm that concerned now that I have it working....
Re: File::Find with sudo from root
by graff (Chancellor) on Jan 19, 2007 at 02:47 UTC
    Personally, I'd feel better doing it like this...
    use strict; my @dirlist = qw(/dir/to/parse); open( FIND, '-|', 'find', @dirlist, qw/-print0/ ) or die "find: $!"; $/ = chr(0); while (<FIND>) { chomp; print "Processing: $_\n"; }
    Depending on what is really supposed to go on inside that while loop, I'd probably add more option flags to the qw// in the open statement (e.g. "-type f" or "-name *.foo" or whatever).

    If the job happens to be traversing any relatively large directory tree (esp. when you get up into tens of thousands of files), you'll most likely be doing yourself a favor (in terms of run-time) by avoiding File::Find.

      I'm not as familar with the "$/ = chr(0);" line. I know it changes the line termintion which I'm assuming is necessary to properly take in the pipe from find. (I haven't had a chance to test difference to see what happens.)

      I did take a quick look in the documentation (http://search.cpan.org/~rgarcia/perl-5.9.4/pod/perlvar.pod) and it seems like it would be a little safer make the change local like:

      #!/usr/bin/perl -w use strict; my @dirlist = qw(/dir/to/parse); open( FIND, '-|', 'find', @dirlist, qw/-print0/ ) or die "find: $!"; { local $/ = chr(0); while (<FIND>) { chomp; print "Processing: $_\n"; } }

      From what I gather form the docs, this will make sure that the $/ change doesn't affect anything else and cause unexpected behavior. PLEASE NOTE, I have not had a chance to test that code, so it may not work in part or whole.

        I'm not as familar with the "$/ = chr(0);" line. I know it changes the line termintion which I'm assuming is necessary to properly take in the pipe from find.

        This is correct. In this example, find gets the -print0 option, which causes it to terminate file names with a null character (chr(0) or "\000") instead of a newline. This is useful because filenames can't contain a null, but they can contain a newline.

        I'd agree also with your use of local to scope the change to $/.