Printing cwd without full path in perl? - perl

I have a bunch of data that is stored in sub-directories labeled by date. I have used the Cwd command to get the Current working directory so that I can then print it to the vi file that I am writing with the recovered data from the sub-directories. I am using the Cwd as a prefix to the data strings. Is there a way to print only the current directory name and not the path?
example:
Instead of printing-
/d2/aschwa/archive_data/METAR_data/20120302KDUX 121255Z.........
Is there a way to print only-
20120302KDUX 121255Z.........
Here's the code I'm using-
use strict;
use warnings;
use file::find;
use Cwd;
my #folder = ("/d2/aschwa/archive_project/METAR_data/");
open( OUT , '>', 'KDUX_METARS.txt') or die "Could not open $!";
print OUT "Station, Day/Time, Obs Type, Wind/Gust, Vis, Sky, T/Td, Alt, Rmk\n";
print STDOUT "Finding METAR files\n";
my $criteria = sub {if(-e && /^2012/) {
open(my $file,$_) or die "Could not open $_ $!\n";
my $dir = getcwd;
while(<$file>) {
print OUT $dir,$_ if /KDUX ....55Z|KDUX ....05Z/;
}
}
};
find($criteria, #folder);
close OUT;

In Perl, you can use functions basename or fileparse to extract the file name from a path.
They are included in the core module File::Basename.

Simply split, then pop.
Shamelessly stolen from perlmonks:
$ perl -e 'print pop #{[split m|/|, "/home/bin/scripts/test.pl"]};'
test.pl
Reference link: http://www.perlmonks.org/?node_id=241089

You can combing the perl module File::Basename with Cwd to get the directory without the path
perl -MCwd -MFile::Basename -e 'my $dir = cwd; print basename($dir)'

Why don't you just get the content after the last slash with a regexp like below:
$path = '/d2/aschwa/archive_data/METAR_data/20120302KDUX 121255Z.........';
$path = $1 if $path =~ m~/([^/]*)/?$~;
This is in my opinion the best way to do it. The above code is just an example, but the regexp there will do the job you want.

Related

Perl, how to choose a directory

I'm trying to determine which of the content of a folder is a directory and which is a file, I wrote the following but the result is not what I would expect:
opendir DH, $dir or die "Cannot open Dir: $!";
my #dirs = grep !/^\.\.?$/, readdir DH ;
foreach my $files (#dirs) {
print $files."<br>";
if ( -d $files )
{
print $files." is a directory<br>";
}
}
closedir DH;
The result is something as the example below:
.file1
file.log
file3.zip
file4
file5.zip
dir1.name1.suffix1.yyyy.MM.dd.hh.mm.ss
file5.zip
file6.tar
dir2
dir3.name1.suffix1.yyyy.MM.dd.hh.mm.ss
where the item starting with dir are actual directory, so my question is why the if is failing discover them as such?
What am I doing wrong?
$diris missing...
if ( -d "$dir/$files" )
{
print $files." is a directory<br>";
}
It's easiest to chdir to $dir so that you don't have to prefix the node names with the path. You can also use autodie if you are running Perl v5.10.1 or better. Finally, if you use $_ as your loop control variable (the file/directory names) you can omit it from the parameters of print, -d and regex matches
Like this
use strict;
use warnings;
use v5.10.1;
use autodie;
my ($dir) = #ARGV;
opendir my $dh, $dir;
chdir $dh;
while ( readdir $dh ) {
next if /\A\.\.?\z/;
print;
print " is a directory" if -d;
print "<br/>\n";
}
... # local expires. working directory returns to its original value
Update
In view of ikegami's (deleted) comment about returning back to the original working directory, here's an example of using the File::chdir module to do this tidily. It exports a tied variable $CWD which will change your working directory if you assign to it. You can also localise it, so just wrapping the above code in braces and adding a new local value for $CWD keeps things neat. Note that File::chdir is not a core module so you will likely need to install it
Note however that there is still a very small possibility that the process may be started with a present working directory that it cannot chdir to. This module will not solve that problem
use strict;
use warnings;
use v5.10.1;
use autodie;
use File::chdir;
my ($dir) = #ARGV;
{
opendir my $dh, $dir;
local $CWD = $dir;
while ( readdir $dh ) {
next if /\A\.\.?\z/;
print;
print " is a directory" if -d;
print "<br/>\n";
}
}

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";
}

How to list out only two specific format files in a folder and print it to a text file

Below Codes is able to read only txt files and print out the filename and size into the filename.txt.
my $directory = 'c:\modules\SFTP';
my $file='filename.txt';
my $OUTFILE;
open $OUTFILE, '>>', $file;
my #files = do {
opendir my $dh, $directory;
grep {/^.*\.txt\z/si} readdir($dh);
};
foreach(#files){
my $filesize = stat($_)->size;
print { $OUTFILE } "$filesize $_" ,"\n";
}
My question is there anyway to read two specific file formats files instead of only one?
Eg. Folder A
textfile1.txt
textfile2.txt
testfile3.fcd
Expected Result:
Able to get and print out the filename and size for two specific formats (.txt and .fcd) files instead of only one (.txt)
What should I change or add in my code so I can get my expected result? Any related post or useful resources to share? Thanks!
You've got most of it right. Make sure you have parens around opendir. use warnings should have pointed that out. Just add another pattern to your grep. Also, open your output file after you have read the directory other wise you will get your output file listed in the output.
use strict;
use warnings;
use autodie;
use File::stat;
my $directory = 'c:\modules\SFTP';
my $file = 'filename.txt';
opendir (my $dh, $directory);
my #files = grep(/^.*\.(txt|fcd)\z/si, readdir $dh);
open my $OUTFILE, '>', $file;
foreach(#files){
my $filesize = stat($_)->size;
print $OUTFILE "$filesize $_" ,"\n";
}
As has already been stated, all you need to filter based off more than one extension is to setup an alteration in your regular expression:
/\.(?:txt|fcd)$/i
Note, that I've simplified your regex a little bit as well since it's not necessary to match against the beginning of the filename.
Enhancement by using Path::Class
I would like to recommend that you look into Path::Class for easier file and path manipulation.
The following does the same thing as your script, but ensures you aren't missing path information in your file test:
use strict;
use warnings;
use autodie;
use Path::Class;
my $dir = dir('c:\modules\SFTP');
open my $outfh, '>>', 'filename.txt';
while (my $file = $dir->next) {
next unless $file =~ /\.(?:txt|fcd)$/;
printf $outfh "%s %s\n", $file->stat->size, $file->basename;
}

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";
}

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.