Reputation: 544
I am working on a Perl script to delete symbolic links in a directory, and I've noticed that the current approach using the find command is taking a considerable amount of time. I'm looking for a more efficient solution.
Here is the current code snippet:
system("find '$dir' -type l -exec rm -f {} \\;");
I've also tried an alternative using unlink and glob:
unlink glob("$dir/*") if -e $dir;
However, both approaches seem to have their drawbacks, and I'm wondering if there's a more optimized way to achieve symbolic link deletion in Perl.
Any insights or suggestions on optimizing the deletion process would be greatly appreciated. Thank you!
Additional Information:
Upvotes: 2
Views: 127
Reputation: 66873
To clean up a single directory, so not recursively, all it takes is something like
-l && unlink for glob "$dir/*";
This is going to be so fast that the performance is kinda hard to measure. Just how many files are you deleting, and how often?
One clear drawback of the above postfix use of the for
loop is that one cannot check errors normally. This is important, specially when deleting a lot of files, so better write it out
for my $file (glob "$dir/*") {
if (-l $file) {
unlink $file or warn "Error unlinking $file: $!"
}
}
Doing it this way does "affect" performance but in a most minuscule way.
I'm wondering ... how many entries altogether (files, dirs, links, etc) are in that directory? If there is an excessive number, like hundreds of thousands, then that by itself may slow down the traversal to a crawl. Scanning larger directories of course takes more time but if the number of entries gets extreme the system may get knocked down on its knees, so to speak; a basic listing from a shell can take 10-20 minutes.
Looking into this at one point I found that if the number is really excessive the system (ls
, find
etc) suffers far more than a program. If that observation holds true generally, or at least on your system as well, then doing it in Perl would again be better.
And if this is really the concern -- excessive number of files -- then two options come to mind.
File::Glob library provides means via its bsd_glob to not sort the filelist
use File::Glob qw(:bsd_glob);
for my $file ( bsd_glob("$dir/*", GLOB_NOSORT) ) {
...
}
This should help some if there is indeed a lot of files. Thanks to Shawn for a comment.
The other possibility is to avoid building the full filelist at once, what is done by using glob
in a list context, like in a for
loop. In scalar context glob
iterates, taking one filename at a time and this is worth trying if you really have quarter million files or some such
while ( my $file = glob "$dir/*" ) {
if (-l $file) {
unlink $file or warn "Error unlinking $file: $!"
}
}
Try and time all these, and clarify how slow this is and how many files -- and links -- you have.
Upvotes: 4
Reputation: 52326
No need to drag external processes like find
and rm
into it. The task can be done purely in perl using File::Find
to traverse the directory tree:
#!/usr/bin/env perl
use warnings;
use strict;
use File::Find;
my $dir = ...; # Fill in the blanks
find(sub { unlink $_ if -l $_ }, $dir);
Upvotes: 5
Reputation: 12337
Use +
instead of ;
, to pass to rm -f
as many arguments as possible, instead of executing it once per each file:
system("find '$dir' -type l -exec rm -f {} \\+");
Use -maxdepth M -mindepth N
if you know how deep in the tree you need to go, for example this searches only the top level:
system("find '$dir' -maxdepth 1 -mindepth 1 -type l -exec rm -f {} \\+");
Upvotes: 4