Extract the numbers at the end from a file name using perl - perl

I am unable to extract the last digits in the filename and rename the file placing the digits at the beginning of the file.
Like suppose my file name is "Gen_TCC_TIF_2110_413010_L3TL_Ae6TL707285_371925.out"
I want to rename the file as "371925_Gen_TCC_TIF_2110_413010_L3TL_Ae6TL707285.out"
my $newFileName ='Gen_TCC_TIF_2110_413010_L3TL_Ae6TL707285_371925.out';
my ($digits) = $newFileName =~ /(\d+)/g;
my $newFileName_2="${digits}_Gen_TCC_TIF_2110_413010_L3TL_Ae6TL707285_371925.out"

try:
$newFileName =~ /(\d+)\.out/;
my $digits = $1;
my $newFileName_2=$digits."_Gen_TCC_TIF_2110_413010_L3TL_Ae6TL707285_371925.out";
(\d+)\.out/ should give you all Digits before .out

Try this:
$newFileName =~ s/(.*?)_(\d+)\.out/$2_$1\.out/;
Or
$newFileName =~ s/(.*?)_(\d+)(\.\w+)/$2_$1$3/;

You can do it with a single regex:
my $newFileName = 'Gen_TCC_TIF_2110_413010_L3TL_Ae6TL707285_371925.out';
my $newFileName_2 = $filename =~ s/(.*)_(\d+)(?=\.out)/$2_$1/r;
# or, for older Perl, use this instead:
(my $newFileName_2 = $filename) =~ s/(.*)_(\d+)(?=\.out)/$2_$1/;
# or, to modify directly the variable $newFileName:
$newFileName =~ s/(.*)_(\d+)(?=\.out)/$2_$1/;
Or if you want to get the digits (because you need them for something else), then you can do:
my ($digits) = $newFileName =~ /_(\d+)\.out/;
You were using /g modifier, which made your regex match all blocks of digits, which isn't what you wanted. (even worst, it was returning an array, but you were only keeping the first element (2110) in the scalar $digit )

Related

pattern matching in perl including files which have signs in their names

Supposed to have an array and I want to find corresponding file in the current directory. I have a problem with pattern matching with those file which have a sign in their names but for the rest everything is OK!
files:
========
A+B-C_www.txt
A-B_CC.books.#1.txt
#!/usr/perl/bin
my #a = qw(A+B-C_www A-B_CC.books.#1);
my $dir = ".";
opendir(DIR, $dir);
my #files = grep(/txt$/,readdir(DIR));
closedir(DIR);
foreach my $file (#files){
for (my $i=0, $i<=$#a, $i++)
if ($file =~ m/$a[$i]){
do some stuff ....}
else{
do some stuff .... }
}
}
This is because regular expressions have special characters with special meanings.
You can fix this with the \Q and \E regex modifiers.
if ($file =~ m/\Q$a[$i]\E/ ) {
Also - turn on use strict; and use warnings;. I assume that the broken regex in your original code (no trailing /) is a typo.
I'd also suggest instead of readdir and grep you could do both with:
while ( my $file = glob ( "*.txt" ) ) {
Also: Your pattern match is a substring match. You may need text anchors if that's not what you intend.
Irrelevant of the pattern matching issues that have already been addressed by the above answers, you could write you code more idiomatically with a grep statement instead of an inner for loop:
while ( my $file = glob('*.txt') ) {
my $has_match = grep { $file =~ m|\Q$_\E| } #a;
if ( $has_match ) {
# do something
}
else {
# do something
}
}
Use quotemeta to escape your pattern before interpolation.
my $pattern = quotemeta($a[$i]);
if ($file =~ m/$pattern/) {
}

perl overload file name download

I need to be able to propose files to be downloaded but i have to read and print the file in my CGI. I tried to go for :
#!/usr/bin/perl -w
use strict;
push( #INC, $lib_directory );
require 'lib_utils.pl';
dl_file('/tmp/final.pdf');
as main page (dl.pl) and
sub dl_file {
my ($file) = #_;
if ( ! -e $file) {
print "file does not exist";
return 0;
}
my $content = read_file( $file, binmode => ':utf8' ) ;
$file =~ m#(.*)([^/]*)$#;
my $directory = $1;
my $filename = $2;
chdir $directory;
my $form = new CGI;
print $form->header(
-type => 'application/octet-stream',
-attachment => $filename,
-filename => $filename,
-Content-Disposition => "attachment; filename=$filename",
);
$form->print($content);
return 1;
}
for the called function. Funny thing is, this code workes just fine if i dont go for a sub and have all the code in dl.pl BUT as soon as i move the code in a sub, the downloaded file is called after the script (ie dl.pl)
How would you change it or how would you do ?
Thanks in advance for your help
Your line
$file =~ m#(.*)([^/]*)$#
will leave $1 containing the whole of $file and $2 empty. You need a slash in there somewhere, probably like this
$file =~ m#(.*)/([^/]*)$#
It would also make sense to make the directory optional, like so
$file =~ m#(?:(.*)/)?([^/]*)$#
my $directory = $1;
and you would have to write
chdir $directory if $directory
This is what's tripping you up:
$file =~ m#(.*)([^/]*)$#;
Looks like you're trying to split "/tmp/final.pdf" into directory and file. But you don't - that pattern splits you into:
print "F:",$filename,"\n";
print "D:",$directory,"\n";
this output:
F:
D:/tmp/final.pdf
This is why you have the problem - you don't have a filename, so it defaults to using the script name.
I would suggest instead you want:
my ( $directory, $filename ) = ( $file =~ m,(.*/)([\.\w]+)$, );
This gives:
F:final.pdf
D:/tmp/
As has been said, you're suffering from the greedy matching of .* which will eat up the entire string:
$file =~ m{(.*)([^/]*)$};
There are three easy solutions to this
1. Boundary Conditions
As has been stated, you can add a boundary condition that limits how much .* can match:
$file =~ m{(?:(.*)/)?([^/]*)$};
my $dir = $1 // '';
my $filename = $2;
Or this somewhat convoluted lookbehind assertion can also enforce a boundary:
$file =~ m{(.*)(?<![^/])([^/]*)$};
my $dir = $1;
my $filename = $2;
2. Non-greedy matching
However, the simplest regex solution is to use non-greedy matching .*?:
$file =~ m{(.*?)([^/]*)$};
my ($dir, $filename) = ($1, $2);
Basically, anytime you're about to put .* anywhere, check your assumptions. The majority of the time you'll actually want .*? instead.
3. Module for parsing file paths
The bonus option is just to use a module like File::Spec parsing file path information
use File::Spec;
my ($vol, $dirs, $filename) = File::Spec->splitpath( $file );

what does eval on directory path do?

$dir =~ s/\$\&/\$src/g;
$pattern =~ s/\$\&/\$src/g;
$dir1 = eval( $dir );
$file = eval( $pattern );
Question about the above, $dir is a directory and $pattern is the pattern of a certain filename. What does eval accomplish? seems like it's replacing $& in the string with the variable &src as a string literal but how does eval on a directory path or filename work?
The fact that these strings happen to contain path-like information is irrelevant to what it's doing.
The 1st 2 lines replace '$&' with '$src'.
The 2nd 2 lines (the evals) force a re-interpolation on the strings, replacing '$src' with the contents of $src. (plus, of course, expanding out any other variables that are named in the strings).
Unless your leaving out something important, these could both be done in a single step by removing the backslash from the substitutions' replacement string, like ($dir1 = $dir) =~ s/\$\&/$src/g

Equating string variables in Perl?

I created a Perl script to extract file name out of a relative path.
Here is a sub which takes the path as an argument then calculates the name from it then appends it to a global variable.
my $all_names = "";
sub check_line
{
my #args = #_;
my $line = $args[0]; #take the path
my #paths = split(/\\|\//, $line); #split according to folder
my $last = #paths;
$last = $last - 1;
my $name = $paths[$last]; #take the file name
chomp($name); #remove \n character from end of string
my ($ext) = $name =~ /(\..+)/; #extract the file extension from file name
if(($ext eq ".h") || ($ext eq ".cpp") || ($ext eq ".c")) #filter the required files
{
$all_names = $all_names . "$name "; #append the file names to global variable
}
}
Now this script is working fine in Perl 5.005 (Yes we have that old version of Perl too!).
But if I run it on Perl 5.10 it wont run properly.
The check for file extension always returns false.
Even when I print the file extension I am getting it properly as .h or .c but even if I compare it individually
if($ext eq ".c")
Then it is returning false.
What might be wrong over here?
Thank You.
This is not because of a change in version.
You probably have a file with CR LF endings, and you're running on a non-Windows machine.
You can fix the line endings with dos2unix or similar.
Alternatively, you could change
chomp($name);
to
$name =~ s/\r?\n\z//;
or better yet
$name =~ s/\s+\z//;

Rename a file with perl

I have a file in a different folder I want to rename in perl, I was looking at a solution earlier that showed something like this:
#rename
for (<C:\\backup\\backup.rar>) {
my $file = $_;
my $new = $file . 'backup' . $ts . '.rar';
rename $file, $new or die "Error, can not rename $file as $new: $!";
}
however backup.rar is in a different folder, I did try putting "C:\backup\backup.rar" in the <> above, however I got the same error.
C:\Program Files\WinRAR>perl backup.pl
String found where operator expected at backup.pl line 35, near "$_ 'backup'"
(Missing operator before 'backup'?)
syntax error at backup.pl line 35, near "$_ 'backup'"
Execution of backup.pl aborted due to compilation errors.
I was using
# Get time
my #test = POSIX::strftime("%m-%d-%Y--%H-%M-%S\n", localtime);
print #test;
To get the current time, however I couldn't seem to get it to rename correctly.
What can I do to fix this? Please note I am doing this on a windows box.
Pay attention to the actual error message. Look at the line:
my $new = $_ 'backup'. #test .'.rar';
If you want to interpolate the contents of $_ and the array #test into a string like that, you need to use:
my $new = "${_}backup#test.rar";
but I have a hard time making sense of that.
Now, strftime returns a scalar. Why not use:
my $ts = POSIX::strftime("%m-%d-%Y--%H-%M-%S", localtime);
my $new = sprintf '%s%s%s.rar', $_, backup => $ts;
Incidentally, you might end up making your life much simpler if you put the time stamp first and formatted it as YYYYMMDD-HHMMSS so that there is no confusion about to which date 04-05-2010 refers.
The line
my $new = $_ 'backup'. #test .'.rar';
probably should read
my $new = $file . 'backup' . #test . '.rar';
(You were missing a concatenation operator, and it is clearer to use the named variable from the line before than reusing $_ there...)
I think you missed the string concat symbol . (the period) :
my $new = $_ 'backup'. #test .'.rar';
should be
my $new = $_ . 'backup' . #test . '.rar';
A slight side issue but you don't need
for (<C:\\backup\\backup.rar>) {
my $file = $_;
.....
}
The < > construct would be useful if you were expanding a wildcard but you are not.
Be thoughtful of future readers of this code (you in a year!) and write
my $file = 'C:\backup\backup.rar' ;
Note the single quotes which doen't expand backslashes.