Sandra Schlichting
Sandra Schlichting

Reputation: 25986

How to get Perl to loop over all files in a directory?

I have a Perl script with contains

open (FILE, '<', "$ARGV[0]") || die "Unable to open $ARGV[0]\n";

while (defined (my $line = <FILE>)) {
  # do stuff
}

close FILE;

and I would like to run this script on all .pp files in a directory, so I have written a wrapper script in Bash

#!/bin/bash
for f in /etc/puppet/nodes/*.pp; do
    /etc/puppet/nodes/brackets.pl $f
done

Question

Is it possible to avoid the wrapper script and have the Perl script do it instead?

Upvotes: 3

Views: 8925

Answers (7)

michael501
michael501

Reputation: 1482

   my @x =   <*>;  

   foreach ( @x ) {
      chomp;
      if ( -f "$_" ) {
         print "process $_\n";  
         # do stuff
         next;
      };

    };

Upvotes: 0

David W.
David W.

Reputation: 107040

Jason Orendorff has the right answer:

From Perlop (I/O Operators)

The null filehandle <> is special: it can be used to emulate the behavior of sed and awk, and any other Unix filter program that takes a list of filenames, doing the same to each line of input from all of them. Input from <> comes either from standard input, or from each file listed on the command line.

This doesn't require opendir. It doesn't require using globs or hard coding stuff in your program. This is the natural way to read in all files that are found on the command line, or piped from STDIN into the program.

With this, you could do:

$ myprog.pl /etc/puppet/nodes/*.pp

or

$ myprog.pl /etc/puppet/nodes/*.pp.backup

or even:

$ cat /etc/puppet/nodes/*.pp | myprog.pl

Upvotes: 2

amon
amon

Reputation: 57600

Yes.

The for f in ...; translates to the Perl

  • for my $f (...) { ... } (in the case of lists) or
  • while (my $f = ...) { ... } (in the case of iterators).

The glob expression that you use (/etc/puppet/nodes/*.pp) can be evaluated inside Perl via the glob function: glob '/etc/puppet/nodes/*.pp'.

Together with some style improvements:

use strict; use warnings;
use autodie;  # automatic error handling

while (defined(my $file = glob '/etc/puppet/nodes/*.pp')) {
  open my $fh, "<", $file;  # lexical file handles, automatic error handling

  while (defined( my $line = <$fh> )) {
    do stuff;
  }
  close $fh;
}

Then:

$ /etc/puppet/nodes/brackets.pl

Upvotes: 8

Sedi
Sedi

Reputation: 71

I would suggest to put all filenames to array and then use this array as parameters list to your perl method or script. Please see following code:

use Data::Dumper
$dirname = "/etc/puppet/nodes";
opendir ( DIR, $dirname ) || die "Error in opening dir $dirname\n";

my @files = grep {/.*\.pp/} readdir(DIR);
print Dumper(@files);

closedir(DIR);

Now you can pass \@files as parameter to any perl method.

Upvotes: 1

Hunter McMillen
Hunter McMillen

Reputation: 61512

Perl can shell out to execute system commands in various ways, the most straightforward is using backticks ``

use strict;
use warnings FATAL => 'all';

my @ls = `ls /etc/puppet/nodes/*.pp`;
for my $f ( @ls ) {
   open (my $FILE, '<', $f) || die "Unable to open $f\n";

   while (defined (my $line = <$FILE>)) {
      # do stuff
   }
   close $FILE;
}

(Note: you should always use strict; and use warnings;)

Upvotes: -2

Sibster
Sibster

Reputation: 3189

take a look at this documentation it explains all you need to know

#!/usr/bin/perl

use strict;
use warnings;

my $dir = '/tmp';

opendir(DIR, $dir) or die $!;

while (my $file = readdir(DIR)) {
# We only want files
next unless (-f "$dir/$file");

# Use a regular expression to find files ending in .pp


   next unless ($file =~ m/\.pp$/);
open (FILE, '<', $file) || die "Unable to open $file\n";

while (defined (my $line = <FILE>)) {
  # do stuff
}
}

closedir(DIR);
exit 0;

Upvotes: 1

Jason Orendorff
Jason Orendorff

Reputation: 45086

This isn’t quite what you asked, but another possibility is to use <>:

while (<>) {
    my $line = $_;
    # do stuff
}

Then you would put the filenames on the command line, like this:

/etc/puppet/nodes/brackets.pl /etc/puppet/nodes/*.pp

Perl opens and closes each file for you. (Inside the loop, the current filename and line number are $ARGV and $. respectively.)

Upvotes: 6

Related Questions