Traversing Through Directories - perl

I am trying to traverse through directories to change certain file extensions in those directories.
I made it to where I can go through a directory that is given through command-line, but I cannot make it traverse through that directories' subdirectories.
For example: If I want to change the file extensions in the directory Test then if Test has a subdirectory I want to be able to go through that directory and change the file extensions of those files too.
I came up with this. This works for one directory. It correctly changes the file extensions of the files in one specific directory.
#!/usr/local/bin/perl
use strict;
use warnings;
my #argv;
my $dir = $ARGV[0];
my #files = glob "${dir}/*pl";
foreach (#files) {
next if -d;
(my $txt = $_) =~ s/pl$/txt/;
rename($_, $txt);
}
I then heard of File::Find::Rule, so I tried to use that to traverse through the directories.
I came up with this:
#!/usr/local/bin/perl
use strict;
use warnings;
use File::Find;
use File::Find::Rule;
my #argv;
my $dir = $ARGV[0];
my #subdirs = File::find::Rule->directory->in( $dir );
sub fileRecurs{
my #files = glob "${dir}/*pl";
foreach (#files) {
next if -d;
(my $txt = $_) =~ s/pl$/txt/;
rename($_, $txt);
}
}
This does not work/ will not work because I am not familiar enough with File::Find::Rule
Is there a better way to traverse through the directories to change the file extensions?

#!/usr/local/bin/perl
use strict;
use warnings;
use File::Find;
my #argv;
my $dir = $ARGV[0];
find(\&dirRecurs, $dir);
sub dirRecurs{
if (-f)
{
(my $txt = $_) =~ s/pl$/txt/;
rename($_, $txt);
}
}
I figured it out with the help of the tutorial #David sent me! Thank you!

Related

Error - For all subdirectories under a main dir - create a list of files within each subdirectory

I am trying to generate individual list of files inside each subdirectory under one given directory. This may be an already addressed and resolved problem, but my specific issue is that my sub directories will always have a pattern in name and also have exact number files for eg -
<**Main_Dir**>
<sub-directory>- sub1_dir with files
sub1.name.txt
sub1.place.txt
sub3.time.txt
sub4.date.txt
<sub-directory>- sub2_dir with files
sub2.name.txt
sub2.place.txt
..............
<sub-directory>- sub3_dir with files
sub3.name.txt
...............
.......
Is there a way to make the code loop over each sub* subfolder, since I know the pattern/name for these main folders will remain like this?
In short the script should create a file under each subdirectory with list of files in it.
Eg -
<sub-directory>- sub1_dir with files
List_sub1_dir.txt
sub1.name.txt
..............
<sub-directory>- sub2_dir with files
List_sub2_dir.txt
...............
My edited code - It does not create any list file in the subfolder Can someone please help me find the error? Thanks a lot!!
use strict;
use warnings;
use File::Find::Rule;
my $directory = './Maindir';
my #subdirs = File::Find::Rule->directory->in( $directory );
foreach my $dir (#subdirs) {
#print "$dir\n";
next if ($dir eq "..");
if (-d $dir)
{
my #files = File::Find::Rule->file() ->name( '*.*' ) ->in( $di
+r);
foreach my $file (#files)
{
open (FH,"$file");
while (<FH>)
{
open FILE,">>./$dir.txt" or die $!;
print FILE "$_";
}
close(FH);
close FILE;
#print "$file\n";
}
}
I think you want
my #files = glob './Main_dir/*/*.txt'
As someone else suggested, File::Find is a useful module for this type of task. Personally, I prefer to use File::Find::Rule instead. Basically File::Find:Rule provides an alternate interface to the File::Find module.
Here's an example using File::Find::Rule to find all files (with full paths) within a directory (and within it's subdirectories):
use strict;
use warnings;
use feature 'say';
use File::Find::Rule;
my $dir = '/mydir';
my $rule = File::Find::Rule->new();
$rule->$file;
my #files = $rule->in($dir);
foreach my $file (#files) {say $file;}
UPDATE:
The above code has a typo. The $rule->$file; line should be $rule->file;.

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.

Find::File to search a directory of a list of files

I'm writing a Perl script and I'm new to Perl -- I have a file that contains a list of files. For each item on the list I want to search a given directory and its sub-directories to find the file return the full path. I've been unsuccessful thus far trying to use File::Find. Here's what I got:
use strict;
use warnings;
use File::Find;
my $directory = '/home/directory/';
my $input_file = '/home/directory/file_list';
my #file_list;
find(\&wanted, $directory);
sub wanted {
open (FILE, $input_file);
foreach my $file (<FILE>) {
chomp($file);
push ( #file_list, $file );
}
close (FILE);
return #file_list;
}
I find File::Find::Rule a tad easier and more elegant to use.
use File::Find::Rule;
my $path = '/some/path';
# Find all directories under $path
my #paths = File::Find::Rule->directory->in( $path );
# Find all files in $path
my #files = File::Find::Rule->file->in( $path );
The arrays contain full paths to the objects File::Find::Rule finds.
File::Find is used to traverse a directory structure in the filesystem. Instead of doing what you're trying to do, namely, have the wanted subroutine read in the file, you should read in the file as follows:
use strict;
use warnings;
use vars qw/#file_list/;
my $directory = '/home/directory/';
my $input_file = '/home/directory/file_list';
open FILE, "$input_file" or die "$!\n";
foreach my $file (<FILE>) {
chomp($file);
push ( #file_list, $file );
}
# do what you need to here with the #file_list array
Okay, well re-read the doc and I misunderstood the wanted subroutine. The wanted is a subroutine that is called on every file and directory that is found. So here's my code to take that into account
use strict;
use warnings;
use File::Find;
my $directory = '/home/directory/';
my $input_file = '/home/directory/file_list';
my #file_list;
open (FILE, $input_file);
foreach my $file (<FILE>) {
chomp($file);
push ( #file_list, $file );
}
close (FILE);
find(\&wanted, $directory);
sub wanted {
if ( $_ ~~ #file_list ) {
print "$File::Find::name\n";
}
return;
}

Perl Recurse through Directory

So essentially what I'm trying to do is go through a directory and perform an action on all of the files, in this case, the sub searchForErrors. This sub works. What I have so far is:
sub proccessFiles{
my $path = $ARGV[2];
opendir(DIR, $path) or die "Unable to open $path: $!";
my #files = readdir(DIR);
#files = map{$path . '/' . $_ } #files;
closedir(DIR);
for (#files){
if(-d $_){
process_files($_);
}
else{
searchForErrors;
}
}
}
proccessFiles($path);
Any help/suggestions would be great. And again, I'm new to Perl, so the more explanation the better. Thank you!
You should use the File::Find module instead of trying to reinvent the wheel:
use strict;
use warnings;
use File::Find;
my #files;
my $start_dir = "somedir"; # top level dir to search
find(
sub { push #files, $File::Find::name unless -d; },
$start_dir
);
for my $file (#files) {
searchForErrors($file);
}
A problem with your current code is that you are including . and .. directories in your recursive search, which will no doubt cause deep recursion errors.
I thought it would be useful to show a Path::Class solution in addition to TLP's File::Find one.
I think this is pretty much self-explanatory.
use strict;
use warnings;
use Path::Class 'dir';
my $root = dir 'C:\path\to\root';
$root->recurse(callback => sub {
my $file = shift;
searchForErrors($file) unless $file->is_dir;
});
A better way of using File::Find without using a lot of memory would be:
use strict; use warnings;
use File::Find;
find(
sub {
searchForErrors($File::Find::name) unless -d
},
"/tmp/"
);
This is more iterator style.

use list of files to to copy and move to new directory in perl

I have a list of file names in a txt file. They correlate to pdfs I have. I would like to copy the actual pdf files that are listed in that list, to a directory. The files that need to be copied are contained in different subdirectories as well. What is the easiest way to do this using perl?
Thanks!
I'd recommend that you read perlfaq5: How do I copy a file?
After that, it shouldn't be too hard; Untested code:
use strict;
use warnings;
use 5.010;
use autodie;
use File::Copy;
use File::Spec;
open my $files_fh, '<', '/path/to/txt';
my $files_dir = shift // '/path/to/dir';
my $destination_dir = shift // '/path/to/dir';
while (<$files_fh>) {
chomp;
next unless -d (my $file = File::Spec->catfile($files_dir, $_) );
copy($file, $file . '.cpy');
move($file . '.cpy', $destination_dir);
say "Copied [$file] and moved it to [$destination_dir]";
}
I would recommend that you use the perl File::Copy package.
use File::Copy;
$filetobecopied = "myhtml.html.";
$newfile = "html/myhtml.html.";
copy($filetobecopied, $newfile) or die "File cannot be copied.";