regular expression is not working - perl

my $pat = '^x.*d$';
my $dir = '/etc/inet.d';
if ( $dir =~ /$pat/xmsg ) {
print "found ";
}
how to make it sucess

Your pattern is looking for strings starting with x (^x) and ending in d (d$). The path you are trying does not match as it doesn't start with x.

You can use YAPE::Regex::Explain to help you understand regular expressions:
use strict;
use warnings;
use YAPE::Regex::Explain;
my $re = qr/^x.*d$/xms;
print YAPE::Regex::Explain->new($re)->explain();
__END__
The regular expression:
(?msx-i:^x.*d$)
matches as follows:
NODE EXPLANATION
----------------------------------------------------------------------
(?msx-i: group, but do not capture (with ^ and $
matching start and end of line) (with .
matching \n) (disregarding whitespace and
comments) (case-sensitive):
----------------------------------------------------------------------
^ the beginning of a "line"
----------------------------------------------------------------------
x 'x'
----------------------------------------------------------------------
.* any character (0 or more times (matching
the most amount possible))
----------------------------------------------------------------------
d 'd'
----------------------------------------------------------------------
$ before an optional \n, and the end of a
"line"
----------------------------------------------------------------------
) end of grouping
----------------------------------------------------------------------
Also, you should not need the g modifier in this case. The documentation has plenty of information about regexes: perlre

There is an 'x' too much :
my $pat = '^.*d$';
my $dir = '/etc/inet.d';
if ( $dir =~ /$pat/xmsg ) {
print "found ";
}

My guess is that you're trying to list all files in /etc/init.d whose name matches the regular expression.
Perl isn't smart enough to figure out that when you name a string variable $dir, assign to it the full pathname of an existing directory, and pattern match against it, you don't intend to match against the pathname,
but against the filenames in that directory.
Some ways to fix this:
perldoc -f glob
perldoc -f readdir
perldoc File::Find
You may just want to use this:
if (glob('/etc/init.d/x*'))
{
warn "found\n";
}

Related

Why below code is not working as expected?

I have below code to match particular keyword from file, Please note that particular keyword is present in that file. (Verified)
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;
my $fname="sample.txt";
my #o_msg_rx;
my $tempStr='=?UTF-8?B?U2Now4PCtm5l?=\, Ma ';
push #o_msg_rx, $tempStr;
foreach my $rx_temp (#o_msg_rx) {
print "rx_temp = $rx_temp\n";
}
my #msg_arr;
open MM, '<', $fname;
chomp(#msg_arr = (<MM>));
close MM;
my (%o_msg_rx, %msg_anti_rx);
foreach my $rx (#o_msg_rx){
($rx =~ s/^!// ? $msg_anti_rx{$rx} : $o_msg_rx{$rx}) = 0 if $rx;
print "rx = \t$rx\n";
print "o_msg_rx_rx = \t$o_msg_rx{$rx}\n";
}
if(#msg_arr) {
foreach my $rx (keys %o_msg_rx) {
$o_msg_rx{$rx} = 1 if grep (/$rx/i, #msg_arr);
}
}
my $regex_ok = (! scalar grep (! $o_msg_rx{$_}, keys %o_msg_rx));
print "regex_ok = $regex_ok\n";
I am attaching few lines from the file for clarification.
# Step: 23 14:48:52
#
# var: expect-count='1'
# var: msg-rx=""=?UTF-8?B?U2Now4PCtm5l?=\, Maik ""
# etc etc etc
Do you intend for $tempStr to be interpreted as a regular expression? If so, then you should know that the ? is a regular expression operator and will not literally match a ? in the target string.
Also, it has a space after Ma but your sample file has Maik so that part won't match.
These changes will produce a different result:
my $tempStr='=?UTF-8?B?U2Now4PCtm5l?=\, Ma'; # remove the extra space
grep (/\Q$rx/i, #msg_arr); # Add \Q to match the literal string $tempStr in regexp
Or you could make $tempStr a real regexp from the start:
my $tempStr=qr/=\?UTF-8\?B\?U2Now4PCtm5l\?=\\, Ma/;
Or you could leave it as a string but put it in regexp syntax (needs an extra doubling of the double backslash, very ugly):
my $tempStr='=\?UTF-8\?B\?U2Now4PCtm5l\?=\\\\, Ma';

Call grep in a way that finds all lines without a specific string?

I am trying to build a Perl program in which uses grep to find specific strings and to avoid others.
The solution for finding the relevant strings is easy:
open(FILE, "<".$file) or next;
my #listOfLines = <FILE>;
close FILE;
my $strToGrep = "strToFind1|strToFind2";
my #results = grep /$strToGrep/, #listOfLines;
But how do I avoid finding no relevant strings?
I want to avoid:
my #subStrToAvoid = qw/ strToAvoid1 strToAvoid2 strToAvoid3 /;
Is there a way you can use Perl's grep in the same way as the
grep utility with certain flags from bash?
Easily - perl's grep function isn't a simple regex grep, it can do all sorts of magic on the default. You can take pretty much any code block that returns a true/false result, and 'feed' it with $_. regex by default will act on $_ so you can have multiple regex conditionals quite easily.
So you can:
#turn your 'avoid' list into a regex
my $avoid = join ( "|", map { quotemeta } #subStrToAvoid );
#note - quotemeta is here in case #subStrToAvoid includes
#regex meta characters like "|" or "."
$avoid = qr/($avoid)/;
#match your regex but exclude the avoid regex
my #results = grep { m/$strToGrep/ and not m/$avoid/ } #listOfLines;
The first parameter to grep is a condition, which can be negated like any other condition with !:
my $ignore = join "|", qw/ strToAvoid1 strToAvoid2 strToAvoid3 /;
my #result = grep !/$ignore/, #listOfLines;
You can chain the greps if needed:
my #out = grep /$ok/, grep !/$notOk/, #list;

How to use "or " operator to assign several values to a variable?

I'm new with perl.
I would like to say that a variable could take 2 values, then I call it from another function.
I tried:
my(#file) = <${dirname}/*.txt || ${dirname}/*.xml> ;
but this seems not working for the second value, any suggestions?
When using the <*> operator as a fileglob operator, you can use any common glob pattern. Available patterns are
* (any number of any characters),
? (any single character),
{a,b,c} (any of the a, b or c patterns),
So you could do
my #file = glob "$dirname/*.{txt,xml}";
or
my #file = (glob("$dirname/*.txt"), glob("$dirname/*.xml"));
or
my #file = glob "$dirname/*.txt $dirname/*.xml";
as the glob pattern is split at whitespace into subpatterns
If I understood correctly, you want #files to fallback on the second option (*.xml) if no *.txt files are found.
If so, your syntax is close. It should be:
my #files = <$dirname/*.txt> || <$dirname/*.xml>;
or
my #files = glob( "$dirname/*.txt" ) || glob( "$dirname/*.xml" );
Also, it's a good idea to check for #files to make sure it's populated (what if you don't have any *.txt or *.xml?)
warn 'No #files' unless #files;
my (#file) = (<${dirname}/*.txt>, <${dirname}/*.xml>);
my(#file) = <${dirname}/*.txt>, <${dirname}/*.xml> ;
<> converts it into an array of file names, so you are essentially doing my #file = #array1, #array2. This will iterate first through txt files and then through xml files.
This works
my $file = $val1 || $val2;
what it means is set $file to $val1, but if $val1 is 0 or false or undef then set $file1 to $val2
In essence, surrounding a variable with < > means either
1) treat it as a filehandle ( for example $read=<$filehandle> )
2) use it as a shell glob (for example #files=<*.xml> )
Looks to me like you wish to interpolate the value $dirname and add either .txt or .xml on the end. The < > will not achieve this
If you wish to send two values to a function then this might be what you want
my #file=("$dirname.txt","$dirname.xml");
then call the function with #file, ie myfunction(#file)
In the function
sub myfunction {
my $file1=shift;
my $file2=shift;
All this stuff is covered in perldocs perlsub and perldata
Have fun

grep in perl without array

If I have one variable : I assigned entire file text to it
$var = `cat file_name`
Suppose in the file , the word 'mine' comes in 17th line (location is not available but just giving example) and I want to search a pattern 'word' after N (eg 10) lines of word 'mine' if pattern 'word' exist in those lines or not. How can i do that in the regular expression without using array'
Example:
$var = "I am good in perl\n but would like to know about the \n grep command in details";
I want to search particular pattern in specific lines (lines 2 to 3 only). How can I do it without using array.
There is a valid case for not using arrays here - when files are prohibitively large.
This is a pretty specific requirement. Rather than beat around the bush to find that Perl idiom, I'd prescribe a subroutine:
sub n_lines_apart {
my ( $file, $n, $first_pattern, $second_pattern ) = #_;
open my $fh, '<', $file or die $!;
my $lines_apart;
while (<$fh>) {
$lines_apart++ if qr/$first_pattern/ .. qr/$second_pattern/;
}
return $lines_apart && $lines_apart <= $n+1;
}
Caveat
The sub above is not designed to handle multiple matches in a single file. Let that be an exercise for the reader.
You can do this with a regular expression match like this:
my $var = `cat $filename`;
while ( $var =~ /foo/g ) {
print $1, "\n";
print "match occurred at position ", pos($var), " in the string.\n";
}
This will print out all the matches of the string 'foo' from your string, similar to grep but not using an array (or list). The /$regexp/g syntax makes the regular expression iteratively match against the string from left to right.
I'd recommend reading perlrequick for a tutorial on matching with regular expressions.
Try this:
perl -ne '$m=$. if !$m && /first-pattern/;
print if $m && ($.-$m >= 2 && $.-$m <= 3) && /second-pattern/'

How to truncate the extension for special case in Perl?

I'm working on a script to truncate all the extensions for a file using the regex as below but it seem doesn't works well as this command does remove some data that I want as it will basically removing everything whenever it see a dot.
The regex I use currently:-
/\..*?$/
It would remove some files like
b10_120.00c.current.all --> b10_120
abc_10.77.log.bac.temp.ls --> abc_10
but I'm looking for an output in b10_120.00c and abc_10.77
Aside from that, is there a way to printout the output such as it keep certain extension only? Such as for the above 2 examples, it will displays b10_120.00c.current and abc_10.77.log. Thank you very much.
The following will strip file name extensions off:
s/\.[^.]+$//;
Explanation
\. matches a literal .
[^.]+ matches every character that is not a .
$ till end of string
Update
my ($new_file_name) = ( $file_name =~ m/^( [^.]+ \. [^.]+ )/x );
Explanation
^ anchor at the start of the string
[^.]+ matches every character that is not a .
\. matches a literal .
[^.]+ matches every character that is not a .
Test
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More 'tests' => 2;
my %file_name_map = (
'b10_120.00c.current.all' => 'b10_120.00c',
'abc_10.77.log.bac.temp.ls' => 'abc_10.77',
);
sub new_file_name {
my $file_name = shift;
my ($new_file_name) = ( $file_name =~ m/^( [^.]+ \. [^.]+ )/x );
return $new_file_name;
}
for my $file_name ( keys %file_name_map ) {
is $file_name_map{$file_name}, new_file_name($file_name),
"Got $file_name_map{$file_name}";
}
$file =~ s/(\.[^.]+).*/$1/; # SO requires 30 chars in answer, that is stupid
You should use \. for the dot in the regular expression.
Also please explain in more details how you want to process file name.
Instead of a regex, I would suggest using this package:
http://perldoc.perl.org/File/Basename.html