How can I copy a directory except for all of the hidden files in Perl? - perl

I have a directory hierarchy with a bunch of files. Some of the directories start with a ..
I want to copy the hierarchy somewhere else, leaving out all files and dirs that start with a .
How can one do that?

I think what you want is File::Copy::Recursive's rcopy_glob():
rcopy_glob()
This function lets you specify a
pattern suitable for perl's glob() as
the first argument. Subsequently each
path returned by perl's glob() gets
rcopy()ied.
It returns and array whose items are
array refs that contain the return
value of each rcopy() call.
It forces behavior as if
$File::Copy::Recursive::CPRFComp is
true.

If you're able to solve this problem without Perl, you should check out rsync. It's available on Unix-like systems, on Windows via cygwin, and perhaps as a stand-alone tool on Windows. It will do what you need and a whole lot more.
rsync -a -v --exclude='.*' foo/ bar/
If you aren't the owner of all of the files, use -rOlt instead of -a.

Glob ignores dot files by default.
perl -lwe'rename($_, "foo/$_") or warn "failure renaming $_: $!" for glob("*")'

The code below does the job in a simple way but doesn't handle symlinks, for example.
#! /usr/bin/perl
use warnings;
use strict;
use File::Basename;
use File::Copy;
use File::Find;
use File::Spec::Functions qw/ abs2rel catfile file_name_is_absolute rel2abs /;
die "Usage: $0 src dst\n" unless #ARGV == 2;
my($src,$dst) = #ARGV;
$dst = rel2abs $dst unless file_name_is_absolute $dst;
$dst = catfile $dst, basename $src if -d $dst;
sub copy_nodots {
if (/^\.\z|^[^.]/) {
my $image = catfile $dst, abs2rel($File::Find::name, $src);
if (-d $_) {
mkdir $image
or die "$0: mkdir $image: $!";
}
else {
copy $_ => $image
or die "$0: copy $File::Find::name => $image: $!\n";
}
}
}
find \&copy_nodots => $src;

cp -r .??*
almost perfect, because it misses files beginning with . and followed by a single sign. like - .d or .e
echo .[!.] .??*
this is even better
or:
shopt -s dotglob ; cp -a * destination; shopt -u dotglob

I found File::Copy::Recursive's rcopy_glob().
The following is what is showed in the docs but is deceptive.
use File::Copy::Recursive qw(fcopy rcopy dircopy fmove rmove dirmove);
it does not import rcopy_glob() and the only way I found to use it was to be explict as follows:
use File::Copy::Recursive;
File::Copy::Recursive::rcopy_glob("glob/like/path","dest/path");

Related

How to use Perl's File::Find::Rule module to find files with 777 perms

I want to use perl File::Find::Rule in order to find files in the server that have perms 777
I know that the module has stats tests so i could simply do this:
$rule->mode(33279)
I found the 33279 by creating a file and printing the permission on it assuming that File::Find::Rule takes decimal? or should it be formatted somehow?
Is this the right approach to have all the file that have exactly the 777 permissions?
this is a script that finds all files on the home dir of a test server.. i want to change it so that it only finds those with 777 permissions.
#!/usr/bin/perl
use strict;
use warnings;
use File::Find::Rule;
my $rule = File::Find::Rule->new;
$rule->file;
$rule->name( '*' );
my #files = $rule->in( "/root" );
for my $file (#files) {
my $mode = (stat $file)[2];
printf ("%04o %s\n",$mode & 07777, $file);
}
The mode includes the file permissions and type. You need to mask it so that you only get the permission bits. Personally I'd implement a custom rule:
use warnings;
use strict;
use File::stat;
use Fcntl qw/S_IMODE/;
use File::Find::Rule 'rule';
my $rule = rule->file->exec(sub{ S_IMODE(stat($_[2])->mode)==0777 });
my #files = $rule->in('/root');
for my $file (#files) {
print $file, "\n";
}
Note that this masked mode still includes the setuid/setgid/sticky bits (often known as Xst). If you want to ignore those too, and check only the ugo/rwx bits, then you'd have to mask against 0777 (e.g. $mode & 0777).
Using File::Find::Rule is cool, but you could do it easily with find and get the answers back in perl:
#files = split /\n/, `/bin/find /root -perm 777`;

How to use additional file in Perl while we make exe

I will add some extra file while I make .exe file using perl (PP). Please see my below code for making .exe file.
pp -gui -a 7z.exe -a 7z.dll -o gui_curl.exe gui_curl.pl
Both file are added when .exe build, But it does not work. I don't know why?
I used both file in my code, like below:-
system("7z.exe a $current_dir/$file_name.tar $current_dir");
system("7z.exe a $current_dir/$file_name.gz $current_dir/$file_name.tar");
Please suggest me how to use this file. I don't want to put this file outside from exe
This is my suggested solution. It does not use 7zip which I have no idea how to make work within pp.
It's based on Archive::Tar instead.
#!/usr/bin/perl
use strict;
use warnings;
use Cwd;
use Archive::Tar;
my $current_dir = getcwd();
my $file_name = 'archive';
my #files_to_archive;
opendir(DIR, $current_dir) or die $!;
while (my $file = readdir(DIR)) {
next if ($file =~ m/^\./);
push #files_to_archive, $file;
}
closedir(DIR);
my $tar = Archive::Tar->new;
$tar->add_files(#files_to_archive);
$tar->write("$current_dir/$file_name.tgz", COMPRESS_GZIP);
my $extract_dir = '/tmp/test_arc';
mkdir $extract_dir unless (-d $extract_dir );
chdir $extract_dir;
$tar->extract('archive.tar');
Afterwards, you run
pp -o zipper.exe archive.pl -M Archive::Tar
and you should have your standalone archiver.

File::Find in Perl - Looking for files only

I have a script like this to list every FILES inside my root path
use strict;
use File::Find qw(find);
my $path = "<my root path>";
find(\&Search, $path);
sub Search{
my $filename = $File::Find::name;
if(-f $filename){
print $filename."\n";
}
}
My point is to try to list all the FILES. However, it also listed the symlink inside my $root. I modify my Search function like this and it worked:
sub Search{
my $filename = $File::Find::name;
#Check if $filename is not symlink first
if(!-l $filename){
if(-f $filename){
print $filename."\n";
}
}
}
But it seem awkward right ? Why do we need two if condition just to verify $filename is the real file and not a symlink !!!
Is there anyone can suggest a better, more decent solution for this ?
Thank you and best regards.
Alex
-f is testing for file, and that includes symlinks. So yes, you do have to test both.
One slightly useful thing, is that you can probably just do:
if ( -f and not -l ) {
because File::Find sets $_ to the current file, and the file tests default to using that too. (won't work if you turn on no_chdir though).
You may also want to consider File::Find::Rule as an alternative to File::Find.
stat and lstat are identical except when it comes to symlinks. The former collects information about the linked file, whereas the latter collects information about the link itself.
The -X EXPR uses stat. lstat is needed here.
sub Search {
my $filename = $File::Find::name;
if (!lstat($filename)) {
warn("Can't stat $filename: $!\n");
return;
}
say $filename if -f _;
}
Bonus: Error checking becomes much simpler when you pre-call stat or lstat.

Perl search for a particular file extension in folder and sub folder

I have a folder which has over 1500 files scattered around in different sub-folders with extension .fna. I was wondering if there is a simple way in Perl to extract all these files and store them in a different location?
As File::Find is recommended everywhere, let me add that there are other, sometimes nicer, options, like https://metacpan.org/pod/Path::Iterator::Rule or Path::Class traverse function.
Which OS are you using? If it's Windows, I think a simple xcopy command would be a lot easier. Open a console window and type "xcopy /?" to get the info on this command. It should be something simple like:
xcopy directory1/*.fna directory2 /s
use File::Find;
my #files;
find(\&search, '/some/path/*.fna');
doSomethingWith(#files);
exit;
sub search {
push #files, $File::Find::name;
return;
}
Without much more information to go on, you don't need a perl script to do something as easy as this.
Here's a *nix one-liner
find /source/dir -name "*.fna" -exec mv -t /target/dir '{}' \+ -print
sorry for the late response. I was away for a conference. Here is my code which seem to work fine so far.
use strict;
use warnings;
use Cwd;
use FileHandle;
open my $out, ">>results7.txt" or die;
my $parent = "/home/denis/Denis_data/Ordered species";
my ($par_dir, $sub_dir);
opendir($par_dir, $parent);
while (my $sub_folders = readdir($par_dir)) {
next if ($sub_folders =~ /^..?$/); # skip . and ..
my $path = $parent . '/' . $sub_folders;
#my $path = $sub_folders;
next unless (-d $path); # skip anything that isn't a directory
chdir($path) or die;
system 'perl batch_hmm.pl';
print $out $path."\n";
#chdir('..') or die;
#closedir($sub_dir);
}
closedir($par_dir);
I will also try the File::Finder option. The above one looks quite messy.

Using a variable as a directory name in perl

I am using a user input variable as my initial directory name, but when trying to expand the directory and create sub-folders, utilizing that variable causes issue in the path.
#!/usr/bin/perl
use strict;
use warnings;
print "What would you like to name your directory?\n";
chomp( my $directory = <STDIN> );
mkdir $directory, 0755;
mkdir $directory/data, 0755; ##<----<<Error begins here##
mkdir $directory/data/image, 0755;
mkdir $directory/data/cache, 0755;
Unquoted / in Perl means division or regular expression match. Quote it if it's part of a string:
mkdir "$directory/data", 0755;
Or try
use File::Path qw/ make_path /;
make_path( "${directory}/data/image", "${directory}/data/cache" );
it will create the intermediate directories for you.
You may also want to look at the Path::Tiny or Path::Class modules, which have a nicer OO-interface for file operations.