in reply to Looping through multiple directories

Your first assignment to @ARGV is actually an I/O command (because it uses <>).

You have specified the file-handle for that I/O as a "wildcard directory", which evaluates to a null file handle.

Update: See Athanasius++ response below - I hade forgotten that <*> is a synonym for glob.

The I/O results in @ARGV being empty, and hanging waiting on STDIN at the next <> operator.

Try changing the first statement to :

@ARGV = glob "/cygdrive/c/Users/abc123/Documents/dude/logs/*.log";
And also, use strict and warnings!.

                We're living in a golden age. All you need is gold. -- D.W. Robertson.

Replies are listed 'Best First'.
Re^2: Looping through multiple directories
by Athanasius (Archbishop) on Dec 26, 2017 at 03:38 UTC

    Hello NetWallah,

    Your first assignment to @ARGV is actually an I/O command (because it uses <>).

    Sorry, no:

    13:35 >perl -MO=Deparse 1848_SoPW.pl use File::Glob (); @ARGV = glob('/cygdrive/c/Users/abc123/Documents/dude/logs/*.log'); while (defined($_ = readline ARGV)) { (); } 1848_SoPW.pl syntax OK 13:35 >

    To dotowwxo:

    From the documentation for glob:

    Note that glob splits its arguments on whitespace and treats each segment as separate pattern. As such, glob("*.c *.h") matches all files with a .c or .h extension.

    So, so specify multiple directories, you simply include each in the expression input to glob. But if you have a lot of directories, it may be more convenient to do something like this:

    my @dirs = qw( dir1 dir2 ); # add more as required @ARGV = (); push @ARGV, <./1848_SoPW/$_/*\.log> for @dirs;

    or, equivalently,

    my @dirs = qw( dir1 dir2 ); @ARGV = map { <./1848_SoPW/$_/*\.log> } @dirs;

    Update: The problem described by NetWallah above will occur if @ARGV should happen to be empty when the while (<>) { loop is entered. So it’s safer to handle this as a special case:

    use strict; use warnings; my @dirs = qw( dir1 dir2 ); @ARGV = map { <./1848_SoPW/$_/*\.log> } @dirs; if (@ARGV) { while (<>) { ... } }

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Hi, thank you so much for your response. May I ask what  map { <./1848_SoPW/$_/*\.log> } @dirs; does?

        Hello again dotowwxo,

        map and grep are two very useful functions that Perl provides for operating on lists. Whereas grep acts as a filter, map acts as a transformer which changes each list element in some way. For example, in this one-liner:

        14:20 >perl -MData::Dump -wE "dd map { $_ * 2 } 1 .. 5;" (2, 4, 6, 8, 10) 17:20 >

        the integers 1, 2, 3, 4, and 5 are doubled.

        By contrast, here’s an example using grep to filter out odd numbers:

        17:20 >perl -MData::Dump -wE "dd grep { $_ % 2 == 0 } 1 .. 10;" (2, 4, 6, 8, 10) 17:26 >

        In both map and grep, each element in the input list is represented in the block by Perl’s default variable $_. So, the expression map { <./1848_SoPW/$_/*\.log> } @dirs takes each directory name in @dirs and globs it in the expression <./1848_SoPW/$_/*\.log>. The result in this case is that map outputs a list of all the names of log files in the given directories.

        Hope that helps,

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Hello, I've tried to your way.. Here's the result

      My code:
      use strict; use warnings; my $dirs1 = </cygdrive/c/Users/attwwwe1/Documents/dude/logs/*.logs>; my $dirs2 = </cygdrive/c/Users/attwwwe1/Documents/dude/logs2/*.logs>; my @dirs = qw($dirs1 $dirs2); @ARGV = map { <./1848_SoPW/$_/*\.log> } @dirs; if (@ARGV){ while(<>){ #somework } } else{ print "none"; }
      It just prints none every single time I run my script... Not sure what's wrong but it doesn't seem to work.

        That’s not my way!

        my $dirs1 = </cygdrive/c/Users/attwwwe1/Documents/dude/logs/*.logs>; my $dirs2 = </cygdrive/c/Users/attwwwe1/Documents/dude/logs2/*.logs>;

        If you’ve decided to glob each directory separately, you need to store the results of each glob operation in an array, not a scalar:

        my @logs1 = </cygdrive/c/Users/attwwwe1/Documents/dude/logs/*.logs>; my @logs2 = </cygdrive/c/Users/attwwwe1/Documents/dude/logs2/*.logs>;

        And then you don’t need to glob again; just put the names of all the log files into @ARGV:

        @ARGV = (@logs1, @logs2);

        Hope that helps,

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

        Instead of two separate globs, you can do it in one, as in

        </path/to/dude/{logs,logs2}/*.logs> # or </path/to/dude/logs*/*.logs> # or even </path/to/dude/*/*.logs>

        Although the usage I showed above should be ok, glob has a few caveats one should be aware of: It does not list filenames beginning with a dot by default, and it splits its argument on whitespace, which may be important in case you are interpolating variables into the pattern - you can use File::Glob ':bsd_glob'; to alleviate the latter caveat. Make sure to read all of glob and File::Glob.