Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

find folders with no further subfolders in perl

Tags:

find

perl

How do I find, in a given path, all folders with no further subfolders? They may contain files but no further folders.

For example, given the following directory structure:

time/aa/
time/aa/bb
time/aa/bb/something/*
time/aa/bc
time/aa/bc/anything/*
time/aa/bc/everything/*
time/ab/
time/ab/cc
time/ab/cc/here/*
time/ab/cc/there/*
time/ab/cd
time/ab/cd/everywhere/*
time/ac/

The output of find(time) should be as follows:

time/aa/bb/something/*
time/aa/bc/anything/*
time/aa/bc/everything/*
time/ab/cc/here/*
time/ab/cc/there/*
time/ab/cd/everywhere/*

* above represents files.

like image 731
Balaji S Avatar asked Nov 21 '25 13:11

Balaji S


1 Answers

Any time you want to write a directory walker, always use the standard File::Find module. When dealing with the filesystem, you have to be able to handle odd corner cases, and naïve implementations rarely do.

The environment provided to the callback (named wanted in the documentation) has three variables that are particularly useful for what you want to do.

$File::Find::dir is the current directory name

$_ is the current filename within that directory

$File::Find::name is the complete pathname to the file

When we find a directory that is not . or .., we record the complete path and delete its parent, which we now know cannot be a leaf directory. At the end, any recorded paths that remain must be leaves because find in File::Find performs a depth-first search.

#! /usr/bin/env perl

use strict;
use warnings;

use File::Find;

@ARGV = (".") unless @ARGV;

my %dirs;
sub wanted {
  return unless -d && !/^\.\.?\z/;
  ++$dirs{$File::Find::name};
  delete $dirs{$File::Find::dir};
}

find \&wanted, @ARGV;
print "$_\n" for sort keys %dirs;

You can run it against a subdirectory of the current directory

$ leaf-dirs time
time/aa/bb/something
time/aa/bc/anything
time/aa/bc/everything
time/ab/cc/here
time/ab/cc/there
time/ab/cd/everywhere

or use a full path

$ leaf-dirs /tmp/time
/tmp/time/aa/bb/something
/tmp/time/aa/bc/anything
/tmp/time/aa/bc/everything
/tmp/time/ab/cc/here
/tmp/time/ab/cc/there
/tmp/time/ab/cd/everywhere

or plumb multiple directories in the same invocation.

$ mkdir -p /tmp/foo/bar/baz/quux
$ leaf-dirs /tmp/time /tmp/foo
/tmp/foo/bar/baz/quux
/tmp/time/aa/bb/something
/tmp/time/aa/bc/anything
/tmp/time/aa/bc/everything
/tmp/time/ab/cc/here
/tmp/time/ab/cc/there
/tmp/time/ab/cd/everywhere
like image 68
Greg Bacon Avatar answered Nov 23 '25 08:11

Greg Bacon



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!