Using Find::File::Rule to find Perl scripts and exclude .bak files - perl

I am using Find::File::Rule to find Perl scripts. I want to exclude certain files, like test files and backup files, but I cannot make it exclude *.bak files if they start with a dot, for example (p.pl):
use warnings;
use strict;
use Data::Dump;
use File::Find::Rule;
open(my $fh,">",".c.bak");
print $fh "#! /usr/bin/env perl\n";
close($fh);
my $rule = File::Find::Rule->new;
$rule->or(
$rule->new->name('*.bak')->prune->discard,
$rule->new->grep( qr/^#!.*perl\s*$/, [ sub { 1 } ] )
);
my #files=$rule->in(".");
dd #files;
This gives output:
("p.pl", ".c.bak")
whereas expected output should be:
"p.pl"

You just have to add another filter rule for the hidden backup files:
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dump;
use File::Find::Rule;
open(my $fh,">",".c.bak");
print $fh "#! /usr/bin/env perl\n";
close($fh);
my $rule = File::Find::Rule->new;
$rule->or(
$rule->new->name('*.bak')->prune->discard,
$rule->new->name('.*.bak')->prune->discard, # <== hidden backups
$rule->new->grep( qr/^#!.*perl\s*$/, [ sub { 1 } ] )
);
my #files=$rule->in(".");
dd #files;
Notice the starting . in the pattern.
This script will produce:
"p.pl"

The problem here is that files prefixed with a dot aren't matched by the '*.bak' quantifier, because they're 'hidden' files.
If you chdir to your directory and do echo * or echo *.bak you won't see the file there either. So effectively - that rule isn't matching, because it's a hidden file.
Solutions would be:
new rule to explicitly match '.' files.
regular expression match to 'name' would do the trick
Something like:
$rule->new->name(qr/\.bak$/)->prune->discard,

Related

How can I access all sub-folders in the current working directory using perl?

I am trying to access all sub-folders in the current working directory. And then I want to run a program in each sub-folder. How can I do this? My code gave the following error:
Too many arguments for glob at ./analysis.pl line 13, near ""08")"
BEGIN not safe after errors--compilation aborted at ./analysis.pl line 13.
#!/usr/bin/perl
use File::chdir;
use strict;
use warnings;
use Cwd;
# current working directory
my $dir = cwd();
# subfolders pathway
my #dirs = glob ($dir/*);
# input file for program
my $data="ethanol.txt";
# enter to each subfolder
foreach $dir ( #dirs ) {
chdir($dir) or die "Cannot cd to $dir: $!\n";
# Run for ethanol
system("echo 1 1 | program -o $data");
chdir("..");
}
I think what you actually meant to say was, glob("$dir/*"), but I like using File::Find::Rule for this type of task:
#!/usr/bin/env perl
use strict;
use warnings;
use Cwd;
use File::Find::Rule;
my $dir = cwd();
my #subdirs = File::Find::Rule->directory
->in( $dir );
foreach my $subdir ( #subdirs ) {
# do stuff
}
I notice that you have loaded File::chdir but fon't use it in your program. It can be a very useful module, and is particularly applicable in this situation. It works by defining a magic variable $CWD that evaluates to the current directory when read, and alters the current directory when written to. That means it can replace Cwd and chdir.
In addition, you can use local to localise changes to the working directory so that the original location is restored at the end of a block.
Take a look at this rewrite of your own code that should do what you need.
use strict;
use warnings;
use File::chdir;
my $data = 'ethanol.txt';
while (my $node = glob '*' ) {
next unless -d $node;
local $CWD = $node;
system "echo 1 1 | program -o $data";
}

selectively run perl script on all **.dat files containing text "some_attr" in directory and sub directories

I want to run a perl script over all .dat files containing text "some_attr" in the specified directory and its sub directories. How can I do that?
I can list all .dat files containing "some_attr" using '***grep -nri some_attr * ./'*
and run perl script manually over greped files,but I want to automate this using perl
Assuming you have a Bash shell, you can use a simple for loop combined with grep:
for file in `grep -lr some_attr | uniq`
do
perl script_name.pl $file
done
You can use File::Find or File::Find::Rule:
use strict;
use warnings;
use autodie;
use File::Find::Rule;
# find all the .dat files in .
my #files = File::Find::Rule->file()
->name( '*.dat' )
->in( '.' );
for my ($file) {
my $data = do {
open my $fh, '<', $file;
local $/;
<$fh>;
};
next if $data !~ /some_attr/;
print $file, "\n";
}

Get names of all the directories with similar naming pattern in Perl

I have a directory "logs" which contains sub-directories as "A1", "A2", "A3", "B1", "B2", "B3".
I want to write a perl code that search all the sub directories with name pattern as "A", i.e. all the directories names starting from character A.
Please help me.
Use the Perl core module File::Find:
use strict;
use warnings;
use File::Find;
#Find in 'logs' directory, assume the script is executed at this folder level
find(\&wanted, 'logs');
sub wanted {
#Subroutine called for every file / folder founded ($_ has the name of the current)
if(-d and /^A/ ) {
print $_, "\n";
}
}
Update:
If you want to parametrize the prefix, you can do this:
use strict;
use warnings;
use File::Find;
my $prefix = 'B';
find(\&wanted, 'logs');
sub wanted {
if(-d and /^$prefix/ ) {
print $_, "\n";
}
}
File::Find is overkill for simply searching a directory. opendir/readdir still has a purpose!
This program does a chdir to the specified directory so that there is no need to build the full path from the names generated by readdir.
The directory to search and the required prefix can be passed as command-line parameters and will default to logs and A if they are not supplied.
use strict;
use warnings;
use autodie;
my ($dir, $prefix) = #ARGV ? #ARGV : qw/ logs A /;
chdir $dir;
my #wanted = do {
opendir(my $dh, '.');
grep { -d and /^\Q$prefix/ } readdir $dh;
};
print "$_\n" for #wanted;

Perl finding a file based off it's extension through all subdirectories

I have a segment of code that is working that finds all of the .txt files in a given directory, but I can't get it to look in the subdirectories.
I need my script to do two things
scan through a folder and all of its subdirectories for a text file
print out just the last segments of its path
For example, I have a directory structed
C:\abc\def\ghi\jkl\mnop.txt
I script that points to the path C:\abc\def\. It then goes through each of the subfolders and finds mnop.txt and any other text file that is in that folder.
It then prints out ghi\jkl\mnop.txt
I am using this, but it really only prints out the file name and if the file is currently in that directory.
opendir(Dir, $location) or die "Failure Will Robertson!";
#reports = grep(/\.txt$/,readdir(Dir));
foreach $reports(#reports)
{
my $files = "$location/$reports";
open (res,$files) or die "could not open $files";
print "$files\n";
}
I do believe that this solution is more simple and easier to read. I hope it is helpful !
#!/usr/bin/perl
use File::Find::Rule;
my #files = File::Find::Rule->file()
->name( '*.txt' )
->in( '/path/to/my/folder/' );
for my $file (#files) {
print "file: $file\n";
}
What about using File::Find?
#!/usr/bin/env perl
use warnings;
use strict;
use File::Find;
# for example let location be tmp
my $location="tmp";
sub find_txt {
my $F = $File::Find::name;
if ($F =~ /txt$/ ) {
print "$F\n";
}
}
find({ wanted => \&find_txt, no_chdir=>1}, $location);
Much easier if you just use File::Find core module:
#!/usr/bin/perl
use strict;
use warnings FATAL => qw(all);
use File::Find;
my $Target = shift;
find(\&survey, #ARGV);
sub survey {
print "Found $File::Find::name\n" if ($_ eq $Target)
}
First argument: pathless name of file to search for. All subsequent arguments are directories to check. File::Find searches recursively, so you only need to name the top of a tree, all subdirectories will automatically be searched as well.
$File::Find::name is the full pathname of the file, so you could subtract your $location from that if you want a relative path.

How do I use chdir to traverse subdirectories and parse XML files?

I want to write a script that traverses a directory and its subdirectories, grabs all the XML files and parses them. I am having trouble with chdir. This works fine:
my $search = "/home/user/books";
chdir($search) or die "cant change dir to $search $!";
system("ls");
But I want the user to decide the path where he want to search it so I am using Getopt::Long:
use strict;
use warnings;
use Data::Dumper;
use XML::Simple;
use Getopt::Long;
my $outputFile = '';
my $searchPath = "";
my $debug = 0;
GetOptions('outputFile=s' => \$outputFile, 'searchPath=s' => \$searchPath);
if ($outputFile eq '' or $searchPath = '') {
die("parameter --outpulFile=s is required.");
}
$searchPath =~ s/\/*$/\//;
my #founddirs = `cd $searchPath`;
foreach my $foundfiles (#founddirs) {
print $foundfiles;
chdir($foundfiles) or die "cant change dir to $searchPath $!";
chdir('..');
}
Command to run:
perl sample.pl --outputFile=books.txt --searchPath=/home/user/june18
I want to grab all the recursive.xml files from the subdirectories and parse them. Does anyone know how this can be done?
A couple of issues here:
$searchPath = '' is setting the search path to an empty string during the input validation. Use eq instead (not ==)
#founddirs will contain nothing since the backtick operator will return nothing. This is because
my #founddirs = `cd $searchPath`;
does not print found directories that are separated by newlines. Perhaps you're after ls $searchPath
On a side note, why not use File::Find instead?
use strict;
use warnings;
use File::Find;
use Getopt::Long;
my $outputFile;
my $searchPath;
GetOptions(
'outputFile=s' => \$outputFile,
'searchPath=s' => \$searchPath,
);
die "Usage : perl sample.pl -outputFile -searchPath\n"
unless $outputFile && $searchPath;
die "No such directory found: $searchPath\n" unless -d $searchPath;
find( sub { print "$File::Find::name\n" if /$outputFile/ }, $searchPath );
#!/usr/bin/perl --
use strict; use warnings;
use Data::Dump qw/ dd /;
use File::Find::Rule qw/ find /;
my #files = find(
file =>
name => '*.xml',
in => \#ARGV
);
dd \#files;
__END__
$ perl ffrule
[]
$ perl ffrule ../soap
[
"../soap/ex1.xml",
"../soap/ex2.xml",
"../soap/ex3.xml",
]