Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Recursion

by Anonymous Monk
on Aug 03, 2001 at 01:58 UTC ( [id://101801]=perlquestion: print w/replies, xml ) Need Help??

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

I have been learning perl for the last two months and now I have presented myself with a problem. I decided to write a Perl script that will basically search my linux box for all files with the suid or sgid buit set starting from the root directory (/). I already know that there will involve some sort of recursion I just can't figure out a good approach to this:
chdir("/")||die "Error:$!\n"; opendir(ROOT_DIR,"/")|| die "Error:$!\n"; foreach $dir_cont (sort readdir(ROOT_DIR)){ next if $dir_cont =~s/^\.\.?$/; if (-f $dir_cont){ log_sbit($dir_cont) if (-u $dir_cont || -g $dir_cont); } elsif (-d $dir_cont && opendir(SUB_DIR,"$dir_cont"){ do_stuff....... }
This is where I get stuck .... I guess I need some kind of recursion or a way to search all directories and their respective sub-directories. Also I need a way for my script to remember where each file (if it has the suid or sgid bit) is located so the log file has:
/usr/local/bin/some_file suid /var/log/.hidden/file sgid /home/greco/bin/be_greco sgid
I know that there are various modules out there (File::Find) that I can utilize, but since I am still in my learning stages, I would like to go on this journey of reinventing the wheel so to speak, so I can better understand perl and learn some things along the way :) All help will be much appreciated.

Replies are listed 'Best First'.
Re: Recursion
by DBX (Pilgrim) on Aug 03, 2001 at 02:09 UTC
    Put your foreach loop into a sub (say it's called search_dir, then change your code to this:

    chdir("/")||die "Error:$!\n"; opendir(ROOT_DIR,"/")|| die "Error:$!\n"; search_dir("/","/"); sub search_dir{ my ($dir,$fullpath) = @_; foreach $dir_cont (sort readdir($dir)){ next if $dir_cont =~s/^\.\.?$/; if (-f $dir_cont){ log_sbit($full_path . $dir_cont) if (-u $dir_cont || -g $dir_cont) +; } elsif (-d $dir_cont && opendir(SUB_DIR,"$dir_cont"){ my $new_full_path = $full_path . $dir_cont; search_dir($dir_cont,$new_full_path); } }

    This code is untested. I just added the $full_path so you could keep track of the full path and I put it all into a sub. So far you have the right idea. You just need to put your logic in a routine you can call over and over again. Once you get it right, check out File::Find.

      next if $dir_cont =~s/^\.\.?$/;
      you might want to change that line to:
      next if $dir_cont=~/^\.\.?$/;
      because I really don't think you want substitution. basically delete the 's' also
      my($dir,$fullpath) = @_;
      should be
      my($dir,$full_path) = @_;
Re: Recursion
by TheoPetersen (Priest) on Aug 03, 2001 at 02:35 UTC
    You could also implement this with an array, instead of a recursive sub:
    opendir(ROOT_DIR,"/")|| die "Error:$!\n"; my @todo = grep {$_ ne '..'} readdir(ROOT_DIR); closedir(ROOT_DIR); while (my $what = shift @todo) { if (-f $what){ log_sbit($what) if (-u $what || -g $what); } elsif (-d $what && opendir(SUB_DIR, $what){ push @todo, map {"$what/$_"} grep {$_ ne '..'} readdir(SUB_DIR); closedir(SUB_DIR); } }
    Untested but perhaps you'll like the approach and work things out. Remember to build full paths each time (that's what the map would be doing if this were working code). When you finally run out of @todo elements you've searched everything.

    And I can't resist: for more information on recursion, see Recursion.

      You know, I must have followed that Link for More Info at least twenty times and i never saw anything new...
        Ah, but each time you saw it, you had the knowledge gained from all the previous times and hence an ever deeper understanding.

          p

Re: Recursion
by tachyon (Chancellor) on Aug 03, 2001 at 17:53 UTC

    OK I like to know how things work so although you can and probably should do this with a module here is how you do it.

    #!/usr/bin/perl -w use strict; my $root = 'c:/cluster1/'; my @dirs = ($root); my @files; for my $path (@dirs){ opendir ( DIR, $path ) or next; # skip dirs we can't read while (my $file = readdir DIR) { # skip the dot files next if $file eq '.' or $file eq '..'; # skip symbolic links to avoid infinite loops next if -l $path.$file; if ( -d $path.$file ) { # add the dirs to our dir list (full path) push @dirs, $path.$file.'/'; } else { # add the files to file list push @files, $path.$file; } } closedir DIR; } print "Directory list\n\n"; print "$_\n" for sort @dirs; print "\n\nFile List\n\n"; print "$_\n" for sort @files;

    How we do it is simple. First we define our root dir and our delimiter and push this into the @dirs array. We then iterate over the @dirs array. Although @dirs initially only contains the root dir we find all its subdirs and push them onto the end of this. Thus after the first iteration @dirs contains all the subdirs of our root dir which we then search for subdirs and so on ad infinitum until we have no more subdirs. Testing for the . and .. dirs is important as these are the current and one level up dirs. Using eq is better than a regex as you can fool a regex using a newline in a dirname (which is valid). We do not want to follow symbolic links otherwise we may end up in an infinite loop. This is what the guys allude to above.

    We then test each file to see if it is a direcory or a file and push it into the appropriate array. By the fime we have iterated over @dirs we have all the directories in @dirs and all the files in @files. These are the fullly specified paths. You can then do whatever you like with them.

    Hope this helps.

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: Recursion
by Hofmator (Curate) on Aug 03, 2001 at 18:31 UTC
Re: Recursion
by Beatnik (Parson) on Aug 03, 2001 at 12:16 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://101801]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others browsing the Monastery: (6)
As of 2024-04-19 09:21 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found