#1
  1. No Profile Picture
    Junior Member
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2000
    Location
    Wheaton,IL USA
    Posts
    9
    Rep Power
    0
    I want to write a loop (probably foreach) that will perform an action on each file in a certain folder. How would I do this?
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Novice (500 - 999 posts)

    Join Date
    Aug 2000
    Location
    Indiana
    Posts
    614
    Rep Power
    15
    $dir="/path/to/dir";
    opendir(DIR, "$dir");
    while($name = readdir(DIR)) {
    ##Put the names in an array, latest files 1st
    unshift(@files, $name);
    }
    closedir(DIR);
    }
    ##Remove "." and ".." (there are better ways to do this, but I didn't want to bother for this simple example)
    pop(@files);
    pop(@files);

    foreach $name (@files) {
    open(COUNT, "$dir/$name");
    #This will put the data into an array
    @data = <COUNT>;
    close(COUNT);
    #do whatever you want to the data now before reinserting it.. I just poped it.
    pop(@data);
    open(DATA, ">$dir/$name")
    foreach $line @data {
    print DATA "$linen";
    }#end foreach
    close(DATA);
    }#end foreach

    I didn't check to see if this works, but it should.
  4. #3
  5. No Profile Picture
    Contributing User
    Devshed Novice (500 - 999 posts)

    Join Date
    Aug 2000
    Location
    Indiana
    Posts
    614
    Rep Power
    15
    One thing to remember: This doesn't care if the "file" is a directory, or an actual file. For that you need to do an if(-f $dir/$file) type thing to see if it is a file before doing anything to it.
  6. #4
  7. No Profile Picture
    freebsd
    Guest
    Devshed Newbie (0 - 499 posts)
    Another way of doing it which I believe is faster.

    ##### Just files in target_dir, files in subdirs of target_dir ignored #####

    #!/usr/local/bin/perl

    #enter the path, that is it.
    $target_dir = "/path_here";

    print "Content-type: text/htmlnn";
    @ls = `ls -A $target_dir`;
    foreach $file (@ls) {
    @files = split (/s+/,$file);
    foreach $result (@files) {
    next if (-d "$target_dir/$result");
    print "$result<br>n";
    }
    }
    exit;


    ##### target_dir and all files in subdirs of target_dir #####

    #!/usr/local/bin/perl

    $target_dir = "/path_here";

    print "Content-type: text/htmlnn";
    @du = `du $target_dir`;
    foreach $line (@du) {
    ($size,$dir) = split (/s+/,$line);
    @ls = `ls -A $dir`;
    foreach $file (@ls) {
    @files = split (/s+/,$file);
    foreach $result (@files) {
    next if (-d "$dir/$result");
    print "$result<br>n";
    }
    }
    }
    exit;


    [This message has been edited by freebsd (edited August 14, 2000).]
  8. #5
  9. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2000
    Posts
    81
    Rep Power
    14
    A more canonical way to do this would be to use some of Perl's higher order functions -- specifically, grep and map. These allow you to apply a block of code to a set of values (kind of like an implicit foreach, but a lot shorter, sweeter, and less prone to errors). So, code to print out all of the files in a directory might look like this:

    <BLOCKQUOTE><font size="1" face="Verdana,Arial,Helvetica">code:</font><HR><pre>
    my $dir = $ENV{HOME};
    opendir DIR, $dir or die "Failed to open dir $dir";
    my @file = map {print "$_n"} grep -f, map "$dir/$_", readdir DIR or die "Failed to open $dir/$_: $!";
    closedir DIR;
    [/code]

    That looks a little intimidating at first, but if you analyse it it isn't so bad. Take a look at perldoc -f grep and perldoc -f map to get some info about those functions (and some examples of their use). It's trivial to adapt that code to perform more complex operations on each file. For example, the following will print out the number of lines in each file:

    <BLOCKQUOTE><font size="1" face="Verdana,Arial,Helvetica">code:</font><HR><pre>
    my $dir = $ENV{HOME};
    opendir DIR, $dir or die "Failed to open dir $dir";
    my @file = map { print &linecount } grep -f, map "$dir/$_", readdir DIR or die "Failed to open $dir/$_: $!";
    closedir DIR;

    sub linecount {
    return `wc -l $_`;
    }
    [/code]

    These aren't tested, but should work (first one definitely, second will probably require a unix system). map and grep are two of the most powerful functions in the Perl language, and it's well worth getting to grips with them.
  10. #6
  11. No Profile Picture
    Contributing User
    Devshed Novice (500 - 999 posts)

    Join Date
    Aug 2000
    Location
    Indiana
    Posts
    614
    Rep Power
    15
    christucker2's is a little more detailed than what I thought he wanted. And freebsd's will always require a UNIX system to work.

    I suggest not doing system command when at all possible to avoid OS incompatibilities.
  12. #7
  13. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2000
    Posts
    81
    Rep Power
    14
    Absolutely. If people didn't use system commands willy nilly in their perl code it would make life a lot easier... Only reason that second example of mine needs unix is for the wordcount example because I couldn't be arsed to write a real subroutine. :-) I would say that it makes sense for whoopnstik to try to learn about things like map and grep though, as they are a common way of achieving the result he/she's looking for in the Perl world, and he/she's likely to come across it again.
  14. #8
  15. No Profile Picture
    Contributing User
    Devshed Novice (500 - 999 posts)

    Join Date
    Aug 2000
    Location
    Indiana
    Posts
    614
    Rep Power
    15
    Agreed

IMN logo majestic logo threadwatch logo seochat tools logo