How can I get sub-folder names in the working directory using perl? - perl

I have a few sub-folders in the main folder. My program will do some calculations in each sub-folder. Firstly the code will create the "result" folder in main folder for all calculations. And, for the calculation in each sub-folder I want to create a folder in the "result" folder. But they should have the same name as sub-folder.
My working directory is "/home/abc/Desktop/test". The "test" is my main folder. There are "a", "b" and "c" sub-folders in "test" folder. My code creates the "result" folder in "test" main folder. But it also should create "a", "b" and "c" sub-folders in "result" folder. How can I fix my code?
#!/usr/bin/env perl
use strict;
use warnings;
use File::Path qw/make_path/;
use Cwd;
my $dir = cwd();
opendir (DIR, $dir) or die "Unable to open current directory! $!\n";
my #subdirs = readdir (DIR) or die "Unable to read directory! $!\n";
closedir DIR;
my $result_path = "$dir/results";
make_path("$result_path");
foreach my $subdir ( sort #subdirs ) {
chdir($subdir) or die "Cannot cd to $dir: $!\n";
make_path("$result_path/$subdir");
system("echo '1 0' | program -f data.mol -o $result_path/$subdir outfile.txt");
chdir("..");
}

I don't think File::Find::Rule is a good choice for this problem. The module's speciality is recursively searching directory trees, and here you just want a list of all the directories in a single folder. That can very simply be done with grep -d, glob '*'
Here's a version that uses the File::chdir module as per your previous question. It avoids the need for Cwd and File::Basename, and it allows you to localise the current working directory so that there is no need for chdir '..' at the end of each loop.
use strict;
use warnings;
use File::chdir;
my #folders = grep -d, glob '*';
my $result_path = "$CWD/result";
mkdir $result_path;
for my $folder ( #folders ) {
my $result_folder = "$result_path/$folder";
mkdir $result_folder;
local $CWD = $folder;
system("echo '1 0' | program -f data.mol -o $result_folder/output.txt");
}

File::Find::Rule->directory->in( $dir );
finds all directories recursively down the directory tree with starting point $dir. For each directory it finds, you are taking the basename.
So, when it comes across $dir/test/a, the basename of that is a, and your code goes ahead and creates result/a.
I suspect you do not need to find all the directories in a tree -- but given your jumbled problem description it is not easy to be certain.
Maybe you just want to opendir the directory, readdir all the entries keeping only directories other than . and .., and closedir when you are done instead of traversing the entire tree under $dir.

Related

Build array of the contents of the working directory in perl

I am working on a script which utilizes files in surrounding directories using a path such as
"./dir/file.txt"
This works fine, as long as the working directory is the one containing the script. However the script is going out to multiple users and some people may not change their working directory and run the script by typing its entire path like this:
./path/to/script/my_script.pl
This poses a problem as when the script tries to access ./dir/file.txt it is looking for the /dir directory in the home directory, and of course, it can't fine it.
I am trying to utilize readdir and chdir to correct the directory if it isn't the right one, here is what I have so far:
my $working_directory = $ENV{PWD};
print "Working directory: $working_directory\n"; #accurately prints working directory
my #directory = readdir $working_directory; #crashes script
if (!("my_script.pl" ~~ #directory)){ #if my_script.pl isnt in #directoryies, do this
print "Adjusting directory so I work\n";
print "Your old directory: $ENV{PWD}\n";
chdir $ENV{HOME}; #make the directory home
chdir "./path/to/script/my_script.pl"; #make the directory correct
print "Your new directory: $ENV{PWD}\n";
}
The line containing readdir crashes my script with the following error
Bad symbol for dirhandle at ./path/to/script/my_script.pl line 250.
which I find very strange because I am running this from the home directory which prints out properly right beforehand and contains nothing to do with the "bad symbol"
I'm open to any solutions
Thank you in advance
The readdir operates with a directory handle, not a path on a string. You need to do something like:
opendir(my $dh, $working_directory) || die "can't opendir: $!";
my #directory = readdir($dh);
Check perldoc for both readdir and opendir.
I think you're going about this the wrong way. If you're looking for a file that's travelling with your script, then what you probably should consider is the FindBin module - that lets you figure out the path to your script, for use in path links.
So e.g.
use FindBin;
my $script_path = $FindBin::Bin;
open ( my $input, '<', "$script_path/dir/file.txt" ) or warn $!;
That way you don't have to faff about with chdir and readdir etc.

How to copy the folder from one directory to another in perl?

I want to copy the folder from one directory to another.
For Example
I have folder in D drive like Sample it that itself contain many folder.I want to copy this sample folder with its sub folders to some other drive.Here i have done something but it copies only the files.
#!/usr/bin/env perl
use strict;
use warnings;
use File::Copy,
my $source_dir = "aa";
my $target_dir = "bb";
opendir(my $DIR, $source_dir) || die "can't opendir $source_dir: $!";
my #files = readdir($DIR);
foreach my $t (#files)
{
if(-f "$source_dir/$t" ) {
#Check with -f only for files (no directories)
copy "$source_dir/$t", "$target_dir/$t";
}
}
closedir($DIR);
Please help with this...
Thanks in advance
You need to use either the File::Copy::Recursive module, which has a number of related functions from which you probably want dircopy; or the File::Mirror module, which has a mirror function that does the same as dircopy, plus a recursive function that allows you to provide a block of code to control exactly how the nodes will be copied.
use strict;
use warnings;
use File::Copy::Recursive qw(dircopy);
dircopy($source_dir,$target_dir) or die("$!\n");

rename the txt file extension using perl

I am doing the below steps:
Read all the text files in a directory and store it in an array named #files
Run a foreach loop on each text file. Extract the file name(stripping of .txt) using split operation and creating a folder of that particular filename. Rename that file to Test.txt (so as to work as input fo another perl executable) Executing test.pl for each file by adding the line require "test.pl";
It works fine for only one file, but not any more. Here is my code:
opendir DIR, ".";
my #files = grep {/\.txt/} readdir DIR;
foreach my $files (#files) {
#fn = split '\.', $files;
mkdir "$fn[0]"
or die "Unable to create $fn[0] directory <$!>\n";
rename "$files", "Test.txt";
require "test3.pl";
rename "Test.txt", "$files";
system "move $files $fn[0]";
}
you don't require the file to be loaded once, but done every time.
So, replace
require "test3.pl";
with
do "test3.pl";
Can you glob for files in that directory..
Replace,
opendir DIR, ".";
my #files = grep {/\.txt/} readdir DIR;
with,
my #files = <*.txt>;

Moving files into different folders/directories based on their name

I have a directory or folder consisting of hundreds of files. They are named and arranged alphabatically. I want to move the files into directories or folders according to the first character of their name (i.e. files starting with a into one folder, files starting with r into another folder, etc).
Is there a way to do it without using CPAN modules?
Are the files all in that one folder, or are they in subfolders? If they are all in a single folder, you can use opendir to access the directory, and then readdir to read the file names and copy them elsewhere (using File::Copy module's move or copy function.
use strict;
use warnings;
use autodie;
use File::Copy; #Gives you access to the "move" command
use constant {
FROM_DIR => "the.directory.you.want.to.read",
TO_DIR => "the.directory.you want.to.move.the.files.to",
};
#Opens FROM_DIR, ao I can read from it
opendir my $dir, FROM_DIR;
# Loopa through the directory
while (my $file = readdir $dir) {
next if ($file eq "." or $file eq "..");
my $from = FROM_DIR . "/" . "$file";
move $from, TO_DIR;
}
This doesn't do exactly what you want, but it should give you the idea. Basically, I'm using opendir and readdir to read the files in the directory and I'm using move to move them to another directory.
I used the File::Copy module, but this is included in all Perl distributions, so it's not a CPAN module that must be installed.
Use glob(), or the built-in File::Find to build a list of files for each starting letter.

Perl - locate the latest subdirectory on a network path and copy the entire contents

I want to locate the latest subdirectory on a network path and copy the entire contents of the latest subdirectory into another folder in the network path
We have lot of subfolders under the folder \\10.184.132.202\projectdump I need to sort the sub folders to get into latest folder and copy the entire contents into another folder on \\10.184.132.203\baseline
I am using the below mentioned script i am able to list the latest modified folder under the directory but I am unaware of copying the contents.
use File::stat;
use File::Copy qw(copy);
$dirname = '\\\\10.184.132.202\\projectdump\\Testing\\';
$destination = '\\\\10.184.132.203\\baseline\\Testing\\';
$timediff=0;
opendir DIR, "$dirname";
while (defined ($sub_dir = readdir(DIR)))
{
if($sub_dir ne "." && $sub_dir ne "..")
{
$diff = time()-stat("$dirname/$sub_dir")->mtime;
if($timediff == 0)
{
$timediff=$diff;
$newest=$sub_dir;
}
if($diff<$timediff)
{
$timediff=$diff;
$newest=$sub_dir;
}
}
}
print $newest,"\n";
open my $in, '<', $newest or die $!;
while (<$in>) {
copy *, $destination; --------> Here i want to copy the entire contents of the $newest to $destination.
}
Use File::Copy::Recursive. This is an optional module, but allows you to copy entire directory trees. Unfortunately, File::Copy::Recursive is not a standard Perl module, but you can install it via the cpan command.
If installing modules is a problem (sometimes it is), you can use the File::Find to go through the directory tree and copy files one at a time.
By the way, you can use forward slashes in Perl for Windows file names, so you don't have to double up on backslashes.
Why don't call a simple shell cmd to find the latest dir?
I think, this will be much simpler in shell...
my $newestdir=`ls -1rt $dirname|tail -n 1`;
in shell:
LATESTDIR=`ls -1rt $dirname|tail -n 1`
cp -r ${LATESTDIR}/* $destination/
Ups, I just realized that you might using Windows...
Get all dirs and their times into a hash then sort that hash reverse order to find the newest one
my ($newest) = sort {$hash{$b} cmp $hash{$a} keys %hash;
then
opendir NDIR, "$newest";
while ($dir=<NDIR>) {
next if $dir eq '.' or $dir eq '..';
copy $dir, $destination;
}