Check if the nested directory structure is empty using perl

Yash Source

My requirement is to check if a nested directory structure is having any binary file or not.

The directory structure looks something like this:

DIR-A
    |
    |--DIR-X
    |       |
    |       |--DIR-X1
    |       |--DIR-X2
    |
    |--DIR-Y
    |       |
    |       |--DIR-Y1
    |       |--DIR-Y2
    |       |--DIR-Y3
    |
    |--DIR-Z
    |       |
    |       |--DIR-Z1
    |       |--DIR-Z2
    |       |--DIR-Z3

At any point in time there can be more directories at Level-1 or Level-2 i.e. there can be some more directories i.e. DIR-P, DIR-Q etc at level-1 and there can be DIR-X3 or DIR-Y4 at level-2.

I have written a sample code but it exits if it finds DIR-X1, Ideally it should exit if there is a binary file inside the directory.

#!/usr/bin/perl

my $someDir = "/path/of/DIR-A";
my @files = ();
my $file;
my $i=0;

opendir(DIR, "$someDir") or die "Cant open $someDir: $!\n";
    @files = readdir(DIR);
    foreach $file(@files) 
    {
          unless ($file =~ /^[.][.]?\z/) 
        {
            print "$i : $file \n";              
            $i++;
            last;
            }
        }

    if ($i != 0) 
    {
        print "The directory contains files! \n";
        exit 1;      
    } 
    else 
    {
        print "This DIR-A is Empty! \n";
        exit 0;
    }

closedir(DIR);

Please suggest me get to the expected solution as below:

read DIR-A
print SUCCESS, if none of the nested directories have a binary file.
print ERROR, if at least one of the nested directories has a binary file.

Thanks!

perlperl-module

Answers

answered 3 months ago Sobrique #1

Use File::Find::Rule

#!/usr/bin/env perl
use strict;
use warnings;

use File::Find::Rule;

my $someDir = "/path/of/DIR-A";

my @files = File::Find::Rule->file()
                            ->name('*.bin')
                            ->in($someDir);

This will get you all files with the extension '.bin'.

If you need to perform a per file test to check that they are 'binary' then you can use grep on your list of @files.

my @files = grep {-B} File::Find::Rule->file()
                                      ->in($someDir);

print "Binary files found\n" if @files;

Also:

  • use strict; use warnings;. It's good.
  • Code formatting is a really good thing. perltidy -pbp makes it easy.

answered 3 months ago Dev123 #2

You can use below script to find if binary file exist or not recursively.

#! /usr/bin/env perl
use warnings;
use strict;
use File::Find;

my $path="/path/of/DIR-A";

sub find_binary {
    my $file = $File::Find::name;

    if (-B $file && ! -d $file) {
        print "ERROR: At least one of the nested directories has a binary file : $file\n";
        exit;
    }
}

find(\&find_binary,$path);
print("SUCCESS: None of the nested directories have a binary file. \n");

answered 3 months ago Guido Flohr #3

Use (warning: my) module File::Globstar:

use v5.10;
use File::Globstar qw(globstar);

my $dir = $ARGV[0] // '.';
say join "\n", grep { -B && ! -d } globstar "$dir/**";

If you want the list of files in a list, assign it instead of printing it:

my @nondirs = grep { -B && ! -d } globstar "$dir/**";

If you know the extender of the files, you can also do this:

my @nondirs = grep { -B && ! -d } globstar "$dir/**/*.png";

Note that the file test -B produces a truthy value for empty files which is maybe not what you want. In that case change the test to -B && -s && ! -d.

answered 3 months ago Javier Elices #4

I am unclear as to what a binary file is for your test. I am assuming that any file found in the directory structure traversed is a binary file. Using File::Find, which is a core module:

use File::Find;

my $error = 0;
find(\&wanted, @ARGV);

if( $error ) {
  print "ERROR, $error files found\n";
}
else {
  print "SUCCESS\n";
}

sub wanted {
  if( -f $_ ) {
    $error++;
  }
}

You may add any test to the wanted function. The find function will invoke the function provided for each file found in the list of directories that is also passed, which will be traversed recursively in depth-first search order (much like the find command does.) Passing it @ARGV you may invoke the script with a list of directories as required (maybe using shell expansion like DIR-*.)

The test function will get the file name being traversed in $_, while the current working directory is set to the directory that contains the file.

comments powered by Disqus