Perl how to provide full or absolute path to fcopy() function - perl

I am new to perl and want to write a basic program to copy the entire directory contents to another directory. My first hurdle is that I need to give the absolute path for source and destination and I can't seem to get the following code to do that:
use strict;
use warnings;
use File::Copy;
use File::Copy::Recursive qw(fcopy rcopy dircopy fmove rmove dirmove);
my $source_dir = "C:\\Tools\\MyTool\\Scripts";
my $destination_dir = "C:\\Tools\\MyTool\\Scripts_Copy";
fcopy($source_dir,$destination_dir) or die $!;
When I execute this, I get the error that the "No such file or directory"

fcopy is only for copying files, dircopy for copying directories.
Use rcopy which decides to use the appropriate function, depending on whether it has to copy a file or a directory.
For a detailed description, please refer to the CPAN documentation of File::Copy::Recursive.

Related

Change symbolic links

I need to change all symbolic links in a given directory to use the shortest relative path.
Example:
change
kat/../kat/link
or
usr/sth/sth/kat/link
into
kat/link
How can I do this using Perl?
You can get a simplified path by using abs_path and then removing the current directory to make it relative:
use warnings;
use strict;
use Cwd qw/getcwd abs_path/;
my $silly_path = 'foo/../foo/../foo/../foo';
my $simplified = abs_path($silly_path);
my $cwd = getcwd();
print "Canonical path: $simplified\n";
print "Current directory: $cwd\n";
$simplified =~ s|^\Q$cwd/||; #Make relative if within current directory.
print "Simplified path: $simplified\n";
This assumes that the links are in Perl's current working directory. You could replace that with another directory if you want. It will result in the relative path for a link within the current directory, or a simplified absolute path for something that points outside the current directory.
You can get all files in a directory using glob, then use the -l $file file test operator to test if $file is a symbolic link.

Perl: how to tar and untar all the contents of a directory including files and subdirectories

I am writing a Perl script and at the end of the script I want to tar all the contents of a specific directory (say selected) as say selected.tar.
Now, I need to untar selected.tar from another perl script in another location to again selected directory.
This is my sheer requirement and I need to preserve all the permission levels and untar should be done in SECURE_EXTRACT_MODE.
In summary please tell me how to tar and untar all the contents of a directory including files and subdirectories.
Any help in this context is highly appreciated.
Please let me know if you need any more details.
Thanks
To do this without shelling out to tar you will need to use
Archive::Tar::Streamed
together with
File::Find. Also, every file must be small enough to be read into the perl process memory in its entirety.
Something like this should do what you want.
use strict;
use warnings;
use autodie;
use File::Find 'find';
use Archive::Tar::Streamed;
open my $fh, '>:raw', 'selected.tar';
my $tar = Archive::Tar::Streamed->new($fh);
find(sub { $tar->add($File::Find::name) }, '/path/to/selected');
close $fh;

relative file paths in perl

I have a perl script which is using relative file paths.
The relative paths seem to be relative to the location that the script is executed from rather than the location of the perl script. How do I make my relative paths relative to the location of the script?
For instance I have a directory structure
dataFileToRead.txt
->bin
myPerlScript.pl
->output
inside the perl script I open dataFileToRead.txt using the code
my $rawDataName = "../dataFileToRead.txt";
open INPUT, "<", $rawDataName;
If I run the perl script from the bin directory then it works fine
If I run it from the parent directory then it can't open the data file.
FindBin is the classic solution to your problem. If you write
use FindBin;
then the scalar $FindBin::Bin is the absolute path to the location of your Perl script. You can chdir there before you open the data file, or just use it in the path to the file you want to open
my $rawDataName = "$FindBin::Bin/../dataFileToRead.txt";
open my $in, "<", $rawDataName;
(By the way, it is always better to use lexical file handles on anything but a very old perl.)
To turn a relative path into an absolute one you can use Cwd :
use Cwd qw(realpath);
print "'$0' is '", realpath($0), "'\n";
Start by finding out where the script is.
Then get the directory it is in. You can use Path::Class::File's dir() method for this.
Finally you can use chdir to change the current working directory to the directory you just identified.
So, in theory:
chdir(Path::Class::File->new(abs_path($0))->dir());
Relative paths are relative to the current working directory. If you don't have any control over the working directory then you need to find a more robust way to spcecify your file paths, e.g. use absolute paths, or perhaps relative paths which are relative to some specific location within the file system.

How do I load libraries relative to the script location in Perl?

How can you get current script directory in Perl?
This has to work even if the script is imported from another script (require).
This is not the current directory
Example:
#/aaa/foo.pl
require "../bbb/foo.pl"
#/bbb/bar.pl
# I want to obtain my directory (`/bbb/`)
print($mydir)
The script foo.pl could be executed in any ways and from any directory, like perl /aaa/foo.pl, or ./foo.pl.
What people usually do is
use FindBin '$Bin';
and then use $Bin as the base-directory of the running script. However, this won't work if you do things like
do '/some/other/file.pl';
and then expect $Bin to contain /some/other/ within that. I'm sure someone thought of something incredibly clever to work this around and you'll find it on CPAN somewhere, but a better approach might be to not include a program within a program, but to use Perl's wonderful ways of code-reuse that are much nicer than do and similar constructs. Modules, for example.
Those generally shouldn't care about what directory they were loaded from. If they really need to operate on some path, you can just pass that path to them.
See Dir::Self CPAN module. This adds pseudo-constant __DIR__ to compliment __FILE__ & __LINE__.
use Dir::Self;
use lib __DIR__ . '/lib';
I use this snippet very often:
use Cwd qw(realpath);
use File::Basename;
my $cwd = dirname(realpath($0));
This will give you the real path to the directory containing the currently running script. "real path" means all symlinks, "." and ".." resolved.
Sorry for the other 4 responses but none of them worked, here is a solution that really works.
In below example that adds the lib directory to include path the $dirname will contain the path to the current script. This will work even if this script is included using require from another directory.
BEGIN {
use File::Spec;
use File::Basename;
$dirname = dirname(File::Spec->rel2abs( __FILE__ )) . "/lib/";
}
use lib $dirname;
From perlfaq8's answer to How do I add the directory my program lives in to the module/library search path?
(contributed by brian d foy)
If you know the directory already, you can add it to #INC as you would for any other directory. You might if you know the directory at compile time:
use lib $directory;
The trick in this task is to find the directory. Before your script does anything else (such as a chdir), you can get the current working directory with the Cwd module, which comes with Perl:
BEGIN {
use Cwd;
our $directory = cwd;
}
use lib $directory;
You can do a similar thing with the value of $0, which holds the script name. That might hold a relative path, but rel2abs can turn it into an absolute path. Once you have the
BEGIN {
use File::Spec::Functions qw(rel2abs);
use File::Basename qw(dirname);
my $path = rel2abs( $0 );
our $directory = dirname( $path );
}
use lib $directory;
The FindBin module, which comes with Perl, might work. It finds the directory of the currently running script and puts it in $Bin, which you can then use to construct the right library path:
use FindBin qw($Bin);
You can also use local::lib to do much of the same thing. Install modules using local::lib's settings then use the module in your program:
use local::lib; # sets up a local lib at ~/perl5
See the local::lib documentation for more details.
Let's say you're looking for script.pl. You may be running it, or you may have included it. You don't know. So it either lies in the %INC table in the first case or as $PROGRAM_NAME (aka $0) in the second.
use strict;
use warnings;
use English qw<$PROGRAM_NAME>;
use File::Basename qw<dirname>;
use File::Spec;
use List::Util qw<first>;
# Here we get the first entry that ends with 'script.pl'
my $key = first { defined && m/\bscript\.pl$/ } keys %INC, $PROGRAM_NAME;
die "Could not find script.pl!" unless $key;
# Here we get the absolute path of the indicated path.
print File::Spec->rel2abs( dirname( $INC{ $key } || $key )), "\n";
Link to File::Basename, File::Spec, and List::Util

How can I copy all files with a given extension?

I need to copy all *.exe files in some directory to other virtual drive .
If I was writing batch script I would do xcopy "%mycopyposition%\*.exe".
But I think it will be a bad idea in Perl script .
I seen a File::Copy module, but couldn't see how to do that.
Try this:
use File::Copy;
for my $file (<*.exe>) {
# Copies from directory $mycopyposition to current directory.
copy "$mycopyposition/$file", $file or die "copy $file failed: $!";
}
I think it is an excellent idea to use xcopy. It does what you want. Plus, it preserves time stamps and other attributes. Has some very useful options.