Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

File::Find - Traverse the first level directories from starting point - How?

by vskatusa (Acolyte)
on May 19, 2020 at 01:34 UTC ( #11116920=perlquestion: print w/replies, xml ) Need Help??

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

Hi, I read with interest the use cases of Find::File here

How do I use Find::File to return only the "first level" directories?

Assume my directory as follows:
/dir1/dir1.1/dir1.2 /dir2/dir2.2/dir2.3 /dir3/dir3.1/dir3.2/dir3.3
I want the following result
dir1 dir2 dir3

Replies are listed 'Best First'.
Re: File::Find - Traverse the first level directories from starting point - How?
by 1nickt (Abbot) on May 19, 2020 at 02:11 UTC

    Hi, welcome to the Monastery,

    That tutorial, while still probably valid, is almost 20 years old. File::Find is still the core Perl module, but there are modern wrappers to make life easier.

    See Path::Tiny which is modern Perl's basic file-handling tool, and which includes an iterator() method as well as a wrapper around that (visit()) which takes a callback to be executed on every file found.

    You didn't show an example of your File::Find code, but I'd rather steer you towards a modern tool anyway, so here's a demo:

    $ ls -d vskatusa/*/*/* vskatusa/blarg/blorg/blech vskatusa/foo/bar/baz $ perl -Mstrict -MPath::Tiny -wE 'say for path("vskatusa")->children' vskatusa/blarg vskatusa/foo $ echo 'frooble' > vskatusa/textfile $ perl -Mstrict -MPath::Tiny -wE 'say for path("vskatusa")->children' vskatusa/blarg vskatusa/textfile vskatusa/foo $ perl -Mstrict -MPath::Tiny -wE 'path("vskatusa")->visit(sub { say if + $_->is_dir });' vskatusa/blarg vskatusa/foo

    For more complex file-finding use cases (which yours is not), see Path::Iterator::Rule by the same author.

    Hope this helps!


    The way forward always starts with a minimal test.
      Thank you very much. For the benefit of others here is the code that worked.
      use Path::Tiny qw/ path cwd /; my $dir = '//192.168.1.44/somedir'; my @files = path( $dir )->children; my @myDirectories = path( $dir )->children; for my $dirName ( @myDirectories ){ if (!( $dirName =~ m/\/\./ )) { # skip dir like /xxxx/.xx print "$dirName\n"; } }

        Great, thanks for the feedback!

        A slightly more idiomatic way of writing your code:

        use strict; use warnings; use Path::Tiny; my $dir = '//192.168.1.44/somedir'; path( $dir )->visit( sub { print "$_\n" if /^[^.]/ && $_->is_dir; });

        Note that your code reads the names of all the files (plain files and directories) into two different variables; also I'm not sure about the regexp, it's not clear from your comment what names you are wanting to skip. The file/dir name at that point in the loop shouldn't have a leading slash IIUC.

        Hope this helps!

        Update: s/all the files/names of all the files/ thx soonix

        The way forward always starts with a minimal test.
Re: File::Find - Traverse the first level directories from starting point - How?
by perlfan (Priest) on May 19, 2020 at 08:11 UTC
    You probably just want a combination of opendir, readdir, and that -d test to make sure $_ is a directory.

    In your case you're just opening and reading the contents of /.

      That would work well enough until ... inevitably ... "the requirements change." (Constants aren't, and variables don't ...) The previously mentioned packages are lightweight and much more future-proof.
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: File::Find - Traverse the first level directories from starting point - How?
by Anonymous Monk on May 19, 2020 at 12:36 UTC
    Maxdepth option controls it Or easier interface my @files = File::Find::Rule->file() ->name( '*.txt', '*.log' ) #can insert regex too ->maxdepth($level) ->in(@LogDirs);
      The following code works
      use Path::Tiny; my $dir = '//192.168.1.44/somedir'; my @files = path( $dir )->children( qr/\.txt\z/ ); #Excludes "." and " +.." automatically. my @myDirectories = path( $dir )->children; for my $dirName ( @myDirectories ){ print "$dirName\n"; }
      However if I change the $dir as follows it does not work (I am aware technically //192.168.1.44 is not a directory but a share)
      use Path::Tiny; my $dir = '//192.168.1.44'; # traverse root my @files = path( $dir )->children( qr/\.txt\z/ ); #Excludes "." and " +.." automatically. my @myDirectories = path( $dir )->children; for my $dirName ( @myDirectories ){ print "$dirName\n"; }
      I get the following error when running the above code
      Error opendir on '//192.168.1.44': Invalid argument at dir.pl line 44.

        \\server\share is the root of the volume. You can't list the contents of the directory \\server because \\server isn't a directory. This has nothing to do with Perl.

        >dir /b \\127.0.0.1 The filename, directory name, or volume label syntax is incorrect. >dir /b \\127.0.0.1\SomeShare a.txt b.txt

        This might be useful.

        Hi, try adding a trailing slash?


        The way forward always starts with a minimal test.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://11116920]
Approved by 1nickt
Front-paged by 1nickt
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (3)
As of 2020-06-05 10:57 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Do you really want to know if there is extraterrestrial life?



    Results (37 votes). Check out past polls.

    Notices?