Click to See Complete Forum and Search --> : perl script for removing directories...


optech
07-26-2001, 04:04 PM
ok, i've been doing perl for about a month now, and i came across something useful to write...
i uninstalled about 45 packages for slackware, but pkgtool doesn't remove empty directories...
so here's what i'm doing:
-checking the whole FS for emtpy directories
-writing the list of empty directories to a file
-allowing the user to edit which directories to move (which will have to be made simpler later)
-then deleting the directories

here's my beef:
how do i check that a directory is empty in PERL?
i was thinking of using du/df to check, but i'm pretty sure there is a better way to do this... anyone know?
i've searched the net for an answer, but i don't even really know what to look for

YaRness
07-26-2001, 04:16 PM
does a directory file have a size 0 when it's empty? if so there's a file test you may be able to use:


if (-z $dir_name)
{
print "$dir_name is empty!\n";
}

YaRness
07-26-2001, 04:16 PM
(also try "perldoc -x" for the rest of the file test commands... there's one to check if a file is a directory as well)

TheLinuxDuck
07-26-2001, 04:42 PM
My first thought would be to opendir the dir, then readdir the contents into an array. Then, check the array count -1 (for . and .. [only -1 since $# of an array is the last slice in the array, which is 1 minus the real count]). If *should* be 0 if there isn't anything else in the dir.


#!/usr/bin/perl -w
use strict;

my(@list);

opendir MYDIR, "/tmp" or die "Cannot open /tmp: $!\n";
@list=readdir MYDIR;
closedir MYDIR;

if($#list-1==0) {
print "Directory is empty\n";
}
else {
print "Directory is not empty\n";
}

exit;


15:38:01 Thu Jul 26 root
/home/root/perl/optech> ls -al /tmp/
total 2
drwxrwxrwt 2 root root 1024 Jul 26 15:37 .
drwxr-xr-x 16 root root 1024 Jun 7 15:24 ..
15:38:03 Thu Jul 26 root
/home/root/perl/optech> ./dircheck.pl
Directory is empty
15:38:06 Thu Jul 26 root
/home/root/perl/optech>

TheLinuxDuck
07-26-2001, 04:56 PM
YaR:

-z returned false for me regardless of whether or not there was anything in the dir.

I also thought maybe stat would work, but both 7 (filesize) and 12 (actual blocks allocated) returned 1024 and 2, respectively, regardless of what was in the dir.

I think the opendir/readdir method is your best bet, unless someone has another idea?

TheLinuxDuck
07-26-2001, 05:48 PM
Just for giggles, I modified the above example and made it loop 1000 for a list of directories to see how long it would take to do each directory.

Then, I rewrote the script, so that it doesn't actually slurp the entire dir's contents into an array.. instead, it uses readdir in a scalar context, which will return a single filename, and when we pass it through the grep (taken directly from 'PP3rdEd' (O'Reilly) p. 770), it will only return a value if whatever it is passed is not a . or ..

That means that there should be a minimum of 3 calls to readdir, which speeds things up quite a bit.

In a speed test on 1000 calls to the check, a directory check was 2 seconds faster in some cases this way.

here is the code:

#!/usr/bin/perl -w
use strict;
#
my($fileCheck);
#
for("/tmp","/etc","/home","/var","/bin","/sbin") {
print "Trying $_: ";
opendir MYDIR, $_ or die "Cannot open $_/: $!\n";
undef $fileCheck;
$fileCheck=1 if(grep !/^.\.?\z/, readdir MYDIR);
closedir MYDIR;
#
if(!defined($fileCheck)) {
print "Directory is empty\n";
}
else {
print "Directory is not empty\n";
}
}
#
exit;


optech, I hope that this helps!

TheLinuxDuck
07-26-2001, 05:50 PM
Also, since I didn't comment it, if anything doesn't make sense, please ask! (^=

optech
07-26-2001, 06:53 PM
beautiful!!!
it was exactly what i needed!!
i'll put your name in the comments when i'm done!!

i'll also post the finished product here...

takshaka
07-27-2001, 01:26 AM
Grr. I now remember how much I hate this brain-dead UBB. Can't edit something with multiple quotes/codes without losing half the message. :mad:

I'll just start from scratch....

[ 27 July 2001: Message edited by: takshaka ]

takshaka
07-27-2001, 01:46 AM
Originally posted by TheLinuxDuck:
Then, I rewrote the script, so that it doesn't actually slurp the entire dir's contents into an array.. instead, it uses readdir in a scalar context,

As an argument to grep() it is still in a list context. You're still cycling through the entire directory.

That means that there should be a minimum of 3 calls to readdir, which speeds things up quite a bit.

Indeed it does:


#!/usr/bin/perl -w
use strict;

# When you want to time something,
use Benchmark;

my @dirs = qw(/tmp /etc /home /var /bin /sbin);

timethese (0, {TLD_GREP => \&grep_tld,
TAK_NOGREP => \&nogrep });

sub nogrep {
for (@dirs) {
print "Trying $_: " if @ARGV;
opendir MYDIR, $_ or do {warn "Cannot open $_/: $!\n"; next};
my $not_empty = -2;
while (readdir MYDIR) {
++$not_empty > 0 and last;
}
closedir MYDIR;

if($not_empty) {
print "Directory is not empty\n" if @ARGV;
}
else {
print "Directory is empty\n" if @ARGV;
}
}
}

sub grep_tld {
my($fileCheck);

for(@dirs) {
print "Trying $_: " if @ARGV;
opendir MYDIR, $_ or do {warn "Cannot open $_/: $!\n"; next};
undef $fileCheck;
$fileCheck=1 if(grep !/^.\.?\z/, readdir MYDIR);
closedir MYDIR;

if(!defined($fileCheck)) {
print "Directory is empty\n" if @ARGV;
}
else {
print "Directory is not empty\n" if @ARGV;
}
}
}

TheLinuxDuck
07-27-2001, 02:38 PM
Duh.. I should have seen that... i actually had first written the code to do only 3 readdir calls, but then I thought, why am I doing that when I can use grep..

So, I plugged grep into it. I originally did something like:

readdir MYDIR for(0..1);
if(readdir MYDIR);


But, I thought the loop of readdir was kinda cheesy, so I changed it. (^= Guess I shoulda just left it.

-- time passes --

Ok.. I redid it again, but this time it uses the above style... with only 3 readdir calls.

However, this sub doesn't use any variables, except for one that takes a parameter, if given, or defaults to $_ if it is defined, or if not, then it defaults to /tmp. Kinda slick..

The ONLY thing I don't like about it is that I can't think of a way to make a call to readdir AND close the filehandle while returning a value based on the readdir. So, I am letting perl take care of it.. that is not my normal style, but I wanted to play. (btw, tak, I borrowed the warn/next bit, hope you don't mind!) (^=


#!/usr/bin/perl -w
use strict;
#
for("/tmp","/etc","/home","/var","/bin","/sbin") {
print "Result: ", emptyDir(), "\n";
}
exit;
#
sub emptyDir
{
my($dirName)=shift || (defined($_)?"$_":"/tmp");
print "Trying $dirName: ";
opendir MYDIR, $dirName or (warn "Cannot open $dirName: $!\n" and next);
readdir MYDIR for(0..1);
return(readdir MYDIR?"not empty":"empty");
}

TheLinuxDuck
07-27-2001, 02:40 PM
I like perl, btw.

optech
07-30-2001, 11:47 AM
Originally posted by TheLinuxDuck:
<STRONG>I like perl, btw.</STRONG>

:D